高效的perl或python算法
采访者在采访中问了一个问题,为下面的函数编写快速高效的算法 问题:编写一个函数来解析以下给定规则的给定字符串,并生成最终解析的字符串作为输出 编写一个函数,该函数将接受字符串作为输入,字符串长度将介于[0..2000000]之间 字符串只能由“A”、“B”和“C”字符组成,如“AAA”、“ABCABC”、“aaaabbabaaccca” 转换规则:高效的perl或python算法,python,algorithm,perl,Python,Algorithm,Perl,采访者在采访中问了一个问题,为下面的函数编写快速高效的算法 问题:编写一个函数来解析以下给定规则的给定字符串,并生成最终解析的字符串作为输出 编写一个函数,该函数将接受字符串作为输入,字符串长度将介于[0..2000000]之间 字符串只能由“A”、“B”和“C”字符组成,如“AAA”、“ABCABC”、“aaaabbabaaccca” 转换规则: 1) “AB”->“AA” 2) “AC”->“AA” 3) “AA”->“A” 4) “CC”->“C” 5) “BC”->“BB” 6) ‘B
1) “AB”->“AA”
2) “AC”->“AA”
3) “AA”->“A”
4) “CC”->“C”
5) “BC”->“BB”
6) ‘BB’->‘B’
每次对给定字符串随机应用上述6条规则,并将最终转换的字符串作为输出 例如,函数的输入是:“ABCAAB”字符串 ABCAAB->AACAAB[AB=AA]
AACAAB->ACAAB[AA=A]
ACAAB->AAAAB[AC=AA]
AAAAB->AAAB[AA=A]
AAAB->AAB[AA=A]
AAB->AB[AA=A]
AB->AA[AB=AA]
AA->A[AA=A]
最终结果:“A”
因为我们现在无法对字符串应用更多规则 我的回答是(伪代码):
有人能为上述问题提出更好的算法和方法吗?您可以在替换匹配转换规则时重复替换
my %h = (
'AB' => 'AA',
'AC' => 'AA',
'AA' => 'A',
'CC' => 'C',
'BC' => 'BB',
'BB' => 'B',
);
my $s = 'ABCAAB';
1 while $s =~ s/(AB|AC|AA|CC|BC|BB)/$h{$1}/; # also without /g switch
print $s;
输出
A
规则1到3将丢弃A后面的任何字符。
规则5和6将放弃a B之后的任何B和C。
规则4将丢弃C之后的任何C。替换顺序无关紧要 因此,在处理后,字符串将是C、CB、CA、CBA、B、BA、A中的一个 对字符串进行一次扫描就足够了。如果你看到一个C,保留它并跳过下一个C;如果你看到一个B,保留它,跳过下一个B;然后,如果你看到一个A,保持它并停止 给出的示例ABCAAB立即产生A
明确应用规则和多次传递的解决方案是不可接受的,因为它们的行为可以是
O(N²)
甚至O(N³)
,而N=2000000000
这里有一个python解决方案:
In [34]: import ranodm
In [35]: rules = {"AB":"AA",'AC':'AA','AA':'A','CC':'C','BC':'BB','BB':'B'}
In [36]: keys = rules.keys()
In [37]: keys
Out[37]: ['AA', 'AC', 'AB', 'BB', 'BC', 'CC']
In [38]: mystr = 'ABCAAB'
In [42]: while len(mystr)>=2:
r = random.choice(keys) #choose one rule randomly
mystr = mystr.replace(r,rules[r])
....:
In [43]: mystr
Out[43]: 'A'
是正确的,模拟它是没有意义的。这似乎是一个骗人的问题,你应该意识到这一点,理解行为并直接实施
Yves的方法可行,但下面是一个类似方法的实际实现:
def transform(string):
a = string.find('A') + 1
b = string.find('B', 0, a or len(string)) + 1
return 'C' * string.startswith('C') + 'B' * (b>0) + 'A' * (a>0)
我搜索第一个'A',然后搜索它左边的'B'(或者在整个字符串中,如果没有'A')。这告诉我‘B’和‘A’是否属于输出。对于“C”,我只需要检查字符串的开头。虽然我可能会像Yves建议的那样扫描整个字符串两次,而不是只扫描一次,但使用find
函数可以让它变得非常快,比仅仅“手动”在字符串上循环并查找“a”(仅在测试字符串的末尾)快大约100倍:
通过使用lstrip('C')
查找第一个非'C'字符,只需一次扫描,这比手动查找要快,但会占用额外的内存,仍然比find
慢得多:
>>> timeit(lambda: s.lstrip('C'), number=1)
2.411250071361735
正则表达式可能也能做到这一点,但即使只是扫描我的测试字符串一次,仅仅查找“A”也要比我的整个转换花费更长的时间:
>>> timeit(lambda: re.search('A', s), number=1)
0.13403880409939006
... 你的问题是什么?@Karoly:请为上述问题提出优化的解决方案/算法。上述代码如何“随机应用所有7条规则”?另外(如果不必是随机的),使用if代替if-else不是更有效吗(因为你不需要while循环)?@Rob:我写循环是因为他们告诉我一次应用一个规则,所以我们不能在模式匹配中使用全局标识符“g”:(我想有一个技巧:替换的顺序无关紧要。@n33rma没有/g开关。让我试试,非常感谢你的解决方案。或者我不能投票给你的答案,它说“你需要15个声望才能投票”@n33rma:现在你可以投票了。你有足够的声望去投票:)非常感谢你的解决方案是的,我试过了,但上面写着“你需要15个声誉才能投票”。对此我很抱歉。谢谢你这么简单。欢迎你。看到长度达到2000000000暗示我有一个技巧。你有一个好主意如何实际实现它吗?在我的回答中,我使用了find()
,因为它非常快。但我需要两次扫描。我想不出一种扫描一次的更快方法。三个if测试和三个while循环。除了病理病例,这里的速度不是问题,解析在第一个A处停止。
>>> timeit(lambda: s.lstrip('C'), number=1)
2.411250071361735
>>> timeit(lambda: re.search('A', s), number=1)
0.13403880409939006