我需要在String中找到最长的非重叠重复子字符串。我有可用字符串的后缀树和后缀数组。
当允许重叠时,答案是微不足道的(后缀树中最深的父节点)。
例如对于String =“acaca”
如果允许重叠,则回答为“aca”,但如果不允许重叠,则回答为“ac”或“ca”。
我只需要算法或高级别的想法。
P.S。:我试过,但我在网上找不到明确的答案。
答案 0 :(得分:4)
生成后缀数组并在O(nlogn)中排序.ps:有更有效的算法,如DC3和Ukkonen算法。 例如:
字符串:ababc
后缀数组:
子串的start-index |子
0 - ababc
2 - abc
1 - babc
3 - bc
4 - c
比较每两个连续的子串并获得具有以下约束的公共前缀:
说 i1 是子字符串索引" ababc ": 0
说 i2 是子字符串的索引" abc ": 2
通用前缀是" ab " ,公共前缀的长度为 len
abs(i1-i2)> len //避免重叠
使用解决方案通过后缀数组,您将获得" ababc"的结果,这是" ab ";
整个解决方案将运行O(nlogn)
然而,会有一个特例:" aaaaa"这个解决方案无法彻底解决 欢迎讨论并提出O(nlogn)但不是O(n ^ 2)
的解决方案答案 1 :(得分:2)
不幸的是,Perkins提出的解决方案不起作用。我们不能强迫我们通过解决方案来找到长重复的非重叠子串。考虑香蕉的后缀树:http://en.wikipedia.org/wiki/Suffix_tree。将首先考虑以“A”作为其父节点的“NA”分支节点,因为它具有最大长度并且是分支节点。但它构造的字符串“ANA”是重叠的,因此它将被拒绝。现在,要考虑的下一个节点是“NA”,它将显示非重叠长度2,但子串“AN”将永远不会被考虑,因为它已经在已经考虑过的ANA字符串中表示。因此,如果您正在搜索所有重复的非重叠子串,或者当您想要第一个按字母顺序排列时,您就会失败。
显然有一种涉及后缀树的方法可行,但更简单的方法在这里列出:http://rubyquiz.com/quiz153.html
希望这有帮助!
答案 2 :(得分:1)
最简单的解决方案是暴力攻击。你有一个算法来找到最长的重叠允许字符串,使用它,检查答案是否有重叠,如果是,找到第二个最长的,检查它是否有重叠,等等。这会将它减少到现有的搜索算法,然后是正则表达式计数操作。
答案 3 :(得分:1)
通过构造后缀树,共享前缀P的所有后缀将成为树中共同祖先的后代。通过存储该子树后缀的最大和最小索引,我们可以保证长度为min(depth,max-min)的重复非重叠子串,其中max-min是它们之间的距离,depth是长度他们共同的前缀。所需的值是具有最大此值的节点。
答案 4 :(得分:0)
这可以使用“计算最长的先前非重叠因子”中给出的结果来解决(参见http://dx.doi.org/10.1016/j.ipl.2010.12.005)
答案 5 :(得分:0)
完整代码:
#include <bits/stdc++.h>
using namespace std;
int cplen(string a,string b){
int i,to=min(a.length(),b.length());
int ret=0;
for(i=0;i<to;i++){
if(a[i]==b[i])ret++;
else {
return ret;}
}
return ret;
}
int main(){
{
int len,i;
string str;
cin>>str;
len=str.length();
vector<pair<string,int> >vv;
map<char,int>hatbc;
string pp="";
for(i=len-1;i>=0;i--){
hatbc[str[i]]++;
pp=str.substr(i,len-i);
vv.push_back(make_pair(pp,i));
}
if(len==1 || (int)hatbc.size()==len){
printf("0\n");
continue;
}
if(hatbc.size()==1){
printf("%d\n",len/2);
continue;
}
char prev=str[0];
int foo=1,koo=0;
for(i=1;i<len;){
while(str[i]==prev && i<len){i++;foo++;}
prev=str[i];
i+=1;
if(koo<foo)koo=foo;
foo=1;
}
sort(vv.begin(),vv.end());
int ans=0;
ans=koo/2;
for(i=1;i<(int)vv.size();i++){
int j=i-1;
int a=vv[j].second,b=vv[i].second;
string sa=vv[j].first,sb=vv[i].first;
int cpl;
cpl=cplen(sa,sb);
if(abs(a-b)>=cpl)
ans=max(ans,cpl);
}
printf("%d\n",ans);
}
return 0;
}
复杂性:O(n * log(n))(由于排序)
答案 6 :(得分:0)
我们使用longest common prefix (LCP) array和后缀数组在O(n log n)时间内解决这个问题。
LCP数组为我们提供了后缀数组中两个连续后缀之间最长的公共前缀。
在构造LCP数组和后缀数组之后,我们可以二进制搜索答案的长度。
假设字符串是&#34; acaca $&#34;。后缀数组在代码片段中以表格形式给出。
<table border="1">
<tr><th>Suffix Array index</th><th>LCP</th><th>Suffix (implicit)</th></tr>
<tr><td>5</td><td>-1</td><td>$</td></tr>
<tr><td>4</td><td>0</td><td>a$</td></tr>
<tr><td>2</td><td>1</td><td>aca$</td></tr>
<tr><td>0</td><td>3</td><td>acaca$</td></tr>
<tr><td>3</td><td>0</td><td>ca$</td></tr>
<tr><td>1</td><td>2</td><td>caca$</td></tr>
</table>
&#13;
让二进制搜索答案的长度。
如果我们有一个答案,那么让两个子字符串对应两个后缀。
无法保证这些后缀在后缀数组中是连续的。但是,如果我们知道子字符串的长度,我们可以看到子字符串的两个后缀之间的LCP表中的每个条目至少是该数字。此外,两个索引之间的差异必须至少为该数字。
猜测子串的长度是一定量,我们可以考虑至少连续运行的LCP数组条目。在每次连续运行中,找到具有最大和最小索引的后缀。
我们怎么知道我们的猜测是下限?
如果某些[连续运行的LCP数组条目中的最大和最小索引之间的距离至少是我们的猜测]至少是我们的猜测,那么,我们的猜测是可达到的下限。
我们怎么知道我们的猜测太大了?
如果所有[连续运行的LCP数组条目中的最大和最小索引之间的距离至少是我们的猜测]小于我们的猜测,那么,我们的猜测太大了。
我们如何根据答案的长度找到答案?
对于每个[连续运行至少是答案的LCP数组条目],找到最低和最高索引。如果它们至少在答案上存在差异,那么我们就会返回最长的非重叠重复子串从这些索引开始。
在你的例子中,&#34; acaca $&#34;,我们可以发现答案的长度是2。
所有运行都是: &#34; aca $&#34;,&#34; acaca $&#34;,并且较低和较高索引之间的距离为2,导致重复的子串&#34; ac&#34;。
&#34; caca $&#34;,&#34; ca $&#34;,并且较低和较高指数之间的距离为2,导致重复的子串&#34; ca&#34;
答案 7 :(得分:0)
由于我很难找到一个有效的算法描述,以便使用后缀树来获取最长的不重叠的重复子字符串,因此,我想分享从各种来源收集的版本。
算法
说明
如果 S 的子字符串在 S 中至少出现两次,则它是两个后缀 S <的公共前缀 P 。 sub> i 和 S j ,其中 i 和 j 分别表示 S 中的起始位置。因此,在 S 的后缀树中存在一个内部节点 v ,该内部节点具有两个后代叶子,分别对应于 i 和 j < / em>,这样从根到 v 的路径的所有边缘标签的串联等于 P 。
最深的此类节点 v (根据其相应前缀的长度)标记了 S 中最长的,可能重叠重复的子字符串。为了确保不考虑重叠的子字符串,我们必须确保 P 不大于 i 和 j 之间的距离。>
因此,我们计算每个节点的最小和最大索引 i min 和 i max 对应于 S 的最左后缀和最右后缀的位置,它们具有相同的前缀。从节点的后代值可以轻松获得其最小和最大索引。 (如果我们要寻找出现至少 k 次的最长子串,则索引计算会更加复杂,因为这时必须考虑所有后代索引的距离,而不仅仅是两个通过仅考虑满足 i min + length( P )≤的前缀 P i max ,我们确保从 S i 开始的 P 足够短,不会与后缀 S j 。
附加说明