是否有正则表达式会找到两个字符串中最长的公共前缀?如果一个正则表达式无法解决这个问题,那么使用regexp(perl,ruby,python,任何东西)的代码或oneliner最优雅的是什么。
PS:我可以通过编程轻松地做到这一点,我要求好奇心,因为在我看来这可以通过regexp解决。PPS:使用正则表达式的O(n)解决方案的额外奖励。来吧,它应该存在!
答案 0 :(得分:27)
如果某个字符不包含任何字符串 - 例如\0
- 您可以写
"$first\0$second" =~ m/^(.*).*\0\1/s;
,最长的公共前缀将保存为$1
。
编辑添加:这显然效率很低。我认为,如果效率是一个问题,那么这根本不是我们应该使用的方法;但我们至少可以通过将.*
更改为[^\0]*
来改善它,以防止无用的贪婪,只需要再次回溯,并将第二个[^\0]*
包裹在(?>…)
中以防止回溯无法帮助。这样:
"$first\0$second" =~ m/^([^\0]*)(?>[^\0]*)\0\1/s;
这将产生相同的结果,但效率更高。 (但仍然没有像 那样有效地作为一种简单的非基于正则表达式的方法。如果字符串都具有 n 的长度,我认为它最坏的情况至少要采取O( n 2 )时间,而直接的非基于正则表达式的方法在中时需要O( n )时间/ em>最糟糕的情况。)
答案 1 :(得分:19)
这是一个Python单行:
>>> a = 'stackoverflow'
>>> b = 'stackofpancakes'
>>> a[:[x[0]==x[1] for x in zip(a,b)].index(0)]
0: 'stacko'
>>> a = 'nothing in'
>>> b = 'common'
>>> a[:[x[0]==x[1] for x in zip(a,b)].index(0)]
1: ''
>>>
答案 2 :(得分:14)
这是使用正则表达式的一种相当有效的方法。代码在Perl中,但原则应该适用于其他语言:
my $xor = "$first" ^ "$second"; # quotes force string xor even for numbers
$xor =~ /^\0*/; # match leading null characters
my $common_prefix_length = $+[0]; # get length of match
(值得注意的是,Perl的字符串XOR运算符(^
)实际上填充了带有空值的较短字符串以匹配较长字符串的长度。因此,如果字符串可能包含空字符,和如果较短的字符串恰好是较长字符串的前缀,则使用此代码计算的公共前缀长度可能超过较短字符串的长度。)
答案 3 :(得分:7)
简单而有效
def common_prefix(a,b):
i = 0
for i, (x, y) in enumerate(zip(a,b)):
if x!=y: break
return a[:i]
答案 4 :(得分:4)
您将遇到的问题是正则表达式一次匹配一个字符串,因此不用于比较两个字符串。
如果有一个字符可以肯定不在任何一个字符串中,你可以使用它将它们分成一个字符串,然后使用对组的反向引用进行搜索。
所以在下面的例子中我使用空格作为分隔符
>>> import re
>>> pattern = re.compile("(?P<prefix>\S*)\S*\s+(?P=prefix)")
>>> pattern.match("stack stable").group('prefix')
'sta'
>>> pattern.match("123456 12345").group('prefix')
'12345'
答案 5 :(得分:3)
这是一个O(N)解决方案,在三元组上有类似Foma的伪代码正则表达式(对于lcp,你有两个输入和一个输出)。为了简单起见,我假设一个二进制字母{a,b}:
def match {a:a:a, b:b:b};
def mismatch {a:b:ε, b:a:ε};
def lcp match* ∪ (match* mismatch (Σ:Σ:ε)*)
现在你只需要一种实现多磁带传感器的语言。
答案 6 :(得分:2)
O(n)解决方案的另一种尝试:
$x=length($first); $_="$first\0$second"; s/((.)(?!.{$x}\2)).*//s;
它取决于。{n}是否被认为是O(1)或O(n),我不知道这是如何有效地实现的。
注意:1。\ 0不应该在任何一个字符串中,它用作分隔符2.结果在$ _
答案 7 :(得分:1)
受Ruakh的回答启发,这是O(n)正则表达式解决方案:
"$first \0$second" =~ m/^(.*?)(.).*\0\1(?!\2)/s;
注: 1.两个字符串都不包含\ 0 2.最长公共前缀将保存为$ 1 3.空间很重要!
编辑:这是不正确的rukach metions,但这个想法是正确的,但我们应该推动regexp机器不要反复检查开头的字母。基本思想也可以在这个perl oneliner中重写。
perl -e ' $_="$first\0$second\n"; while(s/^(.)(.*?)\0\1/\2\0/gs) {print $1;}; '
我想知道它是否可以合并到regexp解决方案中。
答案 8 :(得分:1)
在某些远程情况下可能很有用,所以请点击这里:
RegEx只有3个步骤的解决方案(无法一次创建RegEx ):
字符串A:abcdef
字符串B:abcxef
第一遍:从String A
创建RegEx(第1部分):
匹配:/(.)/g
替换:\1(
结果:a(b(c(d(e(f(
解释演示:http://regex101.com/r/aJ4pY7
第二遍:从1st pass result
创建RegEx
匹配:/^(.\()(?=(.*)$)|\G.\(/g
替换:\1\2)?+
结果:a(b(c(d(e(f()?+)?+)?+)?+)?+)?+
解释演示:http://regex101.com/r/xJ7bK7
第3遍:对String B
中创建的RegEx进行测试2nd pass
匹配:/a(b(c(d(e(f()?+)?+)?+)?+)?+)?+/
结果:abc
(explained demo)
以下是PHP中的美化 单行:
preg_match('/^'.preg_replace('/^(.\()(?=(.*)$)|\G.\(/','\1\2)?+',preg_replace('/(.)/','\1(',$a)).'/',$b,$longest);
答案 9 :(得分:0)
每个迭代解决方案中的非正则表达式,非重复字符串:
def common_prefix(a, b):
#sort strings so that we loop on the shorter one
a, b = sorted((a,b), key=len)
for index, letter in a:
if letter != b[index]:
return a[:index - 1]
return a
答案 10 :(得分:0)
我对regexp的第二个回答(我在评论中建议的优化)。写入简单,但如果第一个字符串很长,则不能简单有效地运行。
这是一个高效的,非正则表达式,可读的单行答案:
$ perl -E '($n,$l)=(0,length $ARGV[0]); while ($n < $l) { $s = substr($ARGV[0], $n, 1); last if $s ne substr($ARGV[1], $n, 1); $n++ } say substr($ARGV[0], 0, $n)' abce abcdef
abc
答案 11 :(得分:0)
在Foma或Xfst中使用扩展正则表达式。
def range(x) x.l;
def longest(L) L - range(range(L ∘ [[Σ:ε]+ [Σ:a]*]) ∘ [a:Σ]*);
def prefix(W) range(W ∘ [Σ* Σ*:ε]);
def lcp(A,B) longest(prefix(A) ∩ prefix(B));
这里最难的部分是定义“最长”。一般来说,要 优化,你构造一组非最佳字符串(恶化)和 然后删除这些(过滤)。
这实际上是一种纯粹的方法,可以避免非常规操作 这样的捕捉。
答案 12 :(得分:-1)
我认为这是效率最低的。没有错误的检查等。
#!/usr/bin/perl
use strict;
use warnings;
my($s1,$s2)=(@ARGV);
#find the shortest string put it into s1, if you will.
my $n=0;
my $reg;
foreach my $c (split(//,$s1)) { $reg .="($c"; $n++;}
$reg .= ")?" x $n;
$s2 =~ /$reg/;
print $&,"\n";
答案 13 :(得分:-1)
这是我针对leetcode问题实施的解决方案:
def max_len(strs):
"""
:type strs: List[str]
:rtype: int
"""
min_s = len(strs[0]);
for s in strs:
if (len(s) < min_s):
min_s = len(s);
return min_s;
class Solution2:
def longestCommonPrefix(self, strs):
"""
:type strs: List[str]
:rtype: str
"""
acc = -1;
test_len = max_len(strs);
for i in range(test_len):
t = strs[0][i];
acc2 = 0;
for j in range(len(strs)):
if (strs[j][i] == t):
acc2 += 1;
if (acc2 == len(strs)):
acc += 1;
if (acc == -1):
return ""
else:
return strs[0][:acc + 1]
希望这会有所帮助