高效的perl或python算法

高效的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

采访者在采访中问了一个问题,为下面的函数编写快速高效的算法

问题:编写一个函数来解析以下给定规则的给定字符串,并生成最终解析的字符串作为输出

编写一个函数,该函数将接受字符串作为输入,字符串长度将介于[0..2000000]之间

字符串只能由“A”、“B”和“C”字符组成,如“AAA”、“ABCABC”、“aaaabbabaaccca”

转换规则:
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