zhxfl字符串.docx
《zhxfl字符串.docx》由会员分享,可在线阅读,更多相关《zhxfl字符串.docx(22页珍藏版)》请在冰豆网上搜索。
zhxfl字符串
字符串
目录
后缀数组2
1.1模板2
1.2两个字符串4
1.2.1最长公共子串hdu14034
1.2.2长度不小于k的公共子串的个数(pku3415)4
1.3重复子串7
1.3.1可以重叠最长子串7
1.3.2不可重叠最长重复子串pku17437
13.3可重叠的k次最长重复子串pku32619
1.4子串的个数10
14.1不相同的子串的个数(spoj694,spoj705)10
1.4.2回文子串Timus129711
1.5连续重复子串12
1.5.1周期字符串的最大重复次数pku2406使用kmp解o
(1)12
1.5.2重复次数最多的连续重复子串(spoj687,pku3693)13
1.5多个字符串14
1.5.1不小于k个字符串中的最长子串(pku3294)14
1.5.2每个字符串至少出现两次且不重叠的最长子串(spoj220)14
1.5.3出现或反转后出现在每个字符串中的最长子串(PKU3294)14
2.KMP14
3.Ac自动机14
后缀数组
1.1模板
#include
#include
#include
#include
usingnamespacestd;
#definemaxn110010*2
intwa[maxn],wb[maxn],wv[maxn],wh[maxn];
intcmp(int*r,inta,intb,intj,intn)
{
intx=r[a+j],y=r[b+j];
if(a+j>=n)x=-1;if(b+j>=n)y=-1;
returnr[a]==r[b]&&x==y;
}
voidda(int*r,int*sa,intn,intm)
{
inti,j,p,*x=wa,*y=wb,*t;
for(i=0;ifor(i=0;ifor(i=1;ifor(i=n-1;i>=0;i--)sa[--wh[x[i]]]=i;
for(j=1,p=1;p{
if(j>=n)break;
for(p=0,i=n-j;ifor(i=0;i=j)y[p++]=sa[i]-j;
for(i=0;ifor(i=0;ifor(i=0;ifor(i=1;ifor(i=n-1;i>=0;i--)sa[--wh[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;ix[sa[i]]=cmp(y,sa[i-1],sa[i],j,n)?
p-1:
p++;
}
return;
}
intr[maxn],sa[maxn],Rank[maxn],height[maxn];
voidcalheight(int*r,int*sa,intn)
{
inti,j,k=0;
for(i=0;ifor(i=0;iif(Rank[i]-1>=0)
for(k?
k--:
0,j=sa[Rank[i]-1];r[i+k]==r[j+k]&&i+kreturn;
}
intGet(intn,int*sa)
{
inti,ans=n-sa[0];
for(i=1;i{
ans+=n-sa[i]-height[i];
}
returnans;
}
intRMQ[maxn],mm[maxn],best[20][maxn];
voidinitRMQ(intn)
{
inti,j,a,b;
for(mm[0]=-1,i=1;i<=n;i++)
mm[i]=((i&(i-1))==0)?
mm[i-1]+1:
mm[i-1];
for(i=1;i<=n;i++)best[0][i]=i;
for(i=1;i<=mm[n];i++)
for(j=1;j<=n+1-(1<
{
a=best[i-1][j];
b=best[i-1][j+(1<<(i-1))];
if(RMQ[a]elsebest[i][j]=b;
}
return;
}
intaskRMQ(inta,intb)
{
intt;
t=mm[b-a+1];
b-=(1<a=best[t][a];
b=best[t][b];
returnRMQ[a]a:
b;
}
intlcp(inta,intb)
{
intt;
a=Rank[a];
b=Rank[b];
if(a>b)swap(a,b);
return(height[askRMQ(a+1,b)]);
}
1.2两个字符串
1.2.1最长公共子串hdu1403
给定一个字符串,询问某两个后缀的最长公共前缀。
算法分析:
按照上面所说的做法,求两个后缀的最长公共前缀可以转化为求某个区间上的最小值。
对于这个RMQ问题可以用O(nlogn)的时间先预处理,以后每次回答询问的时间为O
(1)。
intjudge(inti,intn,int*sa)
{
intt1,t2;
if(sa[i]<=n)t1=1;
elset1=0;
if(sa[i-1]<=n)t2=1;
elset2=0;
if(t1!
=t2)return1;
elsereturn0;
}
charr[maxn],tmp[maxn];
intsa[maxn];
intmain()
{
inti,j,n,m;
while(scanf("%s%s",&r,&tmp)!
=EOF)
{
n=strlen(r);
m=strlen(tmp);
r[n]='';
for(i=0,j=n+1;in=n+m+1;
da(r,sa,n,'{');
calheight(r,sa,n);
intmax=0;
for(i=1;imax&&judge(i,n-m-1,sa))max=height[i];
printf("%d\n",max);
}
return0;
}
1.2.2长度不小于k的公共子串的个数(pku3415)
给定两个字符串A和B,求长度不小于k的公共子串的个数(可以相同)。
样例1:
A=“xx”,B=“xx”,k=1,长度不小于k的公共子串的个数是5。
样例2:
A=“aababaa”,B=“abaabaa”,k=2,长度不小于k的公共子串的个数是22。
算法分析:
基本思路是计算A的所有后缀和B的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k的部分全部加起来。
先将两个字符串连起来,中间用一个没有出现过的字符隔开。
按height值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。
扫描一遍,每遇到一个B的后缀就统计与前面的A的后缀能产生多少个长度不小于k的公共子串,这里A的后缀需要用一个单调的栈来高效的维护。
然后对A也这样做一次。
intf[maxn];
intstack[maxn],top;
longlongsum,ans;
chars[maxn];
intmain()
{
inti,k,n,t,m;
while(scanf("%d",&k)&&k)
{
scanf("%s",s);
m=strlen(s);
s[m]='';
scanf("%s",s+m+1);
n=strlen(s);
for(i=0;ir[m]=129;
da(r,sa,n,130);
calheight(r,sa,n);
height[0]=0;
for(i=0;i{
f[i]=sa[i]height[i]-=k-1;
if(height[i]<0)height[i]=0;
}
height[0]=0;
ans=0;
for(t=0;t<=1;t++)//表示入栈的串
{
top=0,sum=0;
if(f[0]==t)
{
stack[top++]=0;
}
for(i=1;i{
if(f[i]==t)//入栈
{
if(top==0)
{
stack[top++]=0;
}
else
{
if(f[i-1]==t)
{
stack[top++]=height[i];
sum+=height[i];
}
else
{
sum-=stack[top-1];
stack[top-1]=min(height[i],stack[top-1]);
sum+=stack[top-1];
inta=top-2;
if(stack[top-1]==0)
{
top=1;
sum=0;
}
else
while(a>=1&&stack[top-1]<=stack[a])
{
sum-=stack[a]-stack[top-1];
stack[a]=stack[top-1];
a--;
}
}
}
}
else//计算
{
if(f[i-1]!
=t)
{
sum-=stack[top-1];
stack[top-1]=min(stack[top-1],height[i]);
sum+=stack[top-1];
}
else
{
stack[top++]=height[i];
sum+=height[i];
}
inta=top-2;
if(stack[top-1]==0)
{
top=1;
sum=0;
}
else
while(a>=1&&stack[top-1]<=stack[a])
{
sum-=stack[a]-stack[top-1];
stack[a]=stack[top-1];
a--;
}
ans+=sum;
}
}
}
printf("%I64d\n",ans);
}
return0;
}
1.3重复子串
1.3.1可以重叠最长子串
算法分析:
求height[]的最大值即可
1.3.2不可重叠最长重复子串pku1743
给定一个字符串,求最长重复子串,这两个子串不能重叠。
算法分析:
这题比上一题稍复杂一点。
先二分答案,把题目变成判定性问题:
判断是否
存在两个长度为k的子串是相同的,且不重叠。
解决这个问题的关键还是利用
height数组。
把排序后的后缀分成若干组,其中每组的后缀之间的height值都
不小于k。
例如,字符串为“aabaaaab”,当k=2时,后缀分成了4组,如图5
所示。
容易看出,有希望成为最长公共前缀不小于k的两个后缀一定在同一组。
然后对于每组后缀,只须判断每个后缀的sa值的最大值和最小值之差是否不小于k。
如果有一组满足,则说明存在,否则不存在。
整个做法的时间复杂度为O(nlogn)。
boolGet(intk,intn)
{
inti,mmin=sa[0],mmax=sa[0];
for(i=1;iif(height[i]else{
mmin=min(mmin,sa[i]);
mmax=max(mmax,sa[i]);
if(mmax-mmin>=k)return1;
}
}
return0;
}
intx[maxn];
intmain()
{
inti,n;
while(scanf("%d",&n)&&n)
{
for(i=0;iif(n==1)
{
printf("0\n");
continue;
}
for(i=0;in--;
da(r,sa,n,88+88);
calheight(r,sa,n);
intleft=0,right=n/2;
intans=0;
while(left<=right)
{
intmid=(left+right)/2;
if(mid>=0&&Get(mid,n))
{
ans=mid;
left=mid+1;
}
elseright=mid-1;
}
if(ans+1<5)printf("0\n");
elseprintf("%d\n",ans+1);
}
return0;
}
13.3可重叠的k次最长重复子串pku3261
给定一个字符串,求至少出现k次的最长重复子串,这k个子串可以重叠。
算法分析:
这题的做法和上一题差不多,也是先二分答案,然后将后缀分成若干组。
不同的是,这里要判断的是有没有一个组的后缀个数不小于k。
如果有,那么存在k个相同的子串满足条件,否则不存在。
这个做法的时间复杂度为O(nlogn)。
boolGet(intk,intn,intt)
{
inti,num=1;
for(i=1;i{
if(height[i]else
{
num++;
if(num>=t)return1;
}
}
return0;
}
intx[maxn];
intmain()
{
inti,n,k;
while(scanf("%d%d",&n,&k)!
=EOF)
{
for(i=0;ida(r,sa,n,1000001);
calheight(r,sa,n);
intleft=0,right=n,ans=0;
while(left<=right)
{
intmid=(left+right)/2;
if(mid>=0&&Get(mid,n,k))
{
ans=mid;
left=mid+1;
}
elseright=mid-1;
}
printf("%d\n",ans);
}
return0;
}
1.4子串的个数
14.1不相同的子串的个数(spoj694,spoj705)
给定一个字符串,求不相同的子串的个数。
算法分析:
每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。
如果所有的后缀按照suffix(sa[1]),suffix(sa[2]),suffix(sa[3]),……,suffix(sa[n])的顺序计算,不难发现,对于每一次新加进来的后缀suffix(sa[k]),它将产生n-sa[k]+1个新的前缀。
但是其中有height[k]个是和前面的字符串的前缀是相同的。
所以suffix(sa[k])将“贡献”出n-sa[k]+1-height[k]个不同的子串。
累加后便是原问题的答案。
这个做法的时间复杂度为O(n)。
intGet(intn,int*sa)
{
inti,ans=n-sa[0];
for(i=1;ireturnans;
}
chars[maxn];
intmain()
{
inti,n,k,T;
scanf("%d",&T);
while(T--)
{
scanf("%s",&s);
n=strlen(s);
for(i=0;ida(r,sa,n,130);
calheight(r,sa,n);
printf("%d\n",Get(n,sa));
}
return0;
}
1.4.2回文子串Timus1297
chars[maxn];
structNode
{
intleft,right;
intmin;
}node[maxn*3];
voidbuild(intn,intleft,intright)
{
node[n].left=left;
node[n].right=right;
if(left==right)
{
node[n].min=height[left];
return;
}
intmid=(left+right)/2;
build(n*2,left,mid);
build(n*2+1,mid+1,right);
if(node[n*2].min>node[n*2+1].min)node[n].min=node[n*2+1].min;
elsenode[n].min=node[n*2].min;
}
intqur(intn,intleft,intright)
{
intmid=(node[n].right+node[n].left)/2;
if(node[n].left==node[n].right)returnnode[n].min;
if(node[n*2].right>=right)returnqur(n*2,left,right);
elseif(node[n*2+1].left<=left)returnqur(n*2+1,left,right);
elsereturnmin(qur(n*2,left,mid),qur(n*2+1,mid+1,right));
}
intmain()
{
inti,n,j;
while(scanf("%s",&s)!
=EOF)
{
n=strlen(s);
for(i=0;ir[n]=59;
for(i=n+1,j=n-1;i<2*n+1;j--,i++)r[i]=s[j]-'A'+1;//倒过来接在后面
da(r,sa,n*2+1,60);
calheight(r,sa,n*2+1);
build(1,1,n*2);
intMax=0;
//回文为奇数
intfirst=0;
for(i=0;i{
intx=Rank[i];
inty=Rank[2*n-i];
if(x>y)swap(x,y);
intt=qur(1,x+1,y);
t=t*2-1;
if(t>Max)
{
Max=t;
first=i-(t)/2;
}
}
//回文为偶数
for(i=0;i{
intx=Rank[2*n-i];
inty=Rank[i+1];
if(x>y)swap(x,y);
intt=qur(1,x+1,y);
t=t*2;
if(t>Max)
{
Max=t;
first=i-t/2+1;
}
}
for(i=first;i}
return0;
}
1.5连续重复子串
1.5.1周期字符串的最大重复次数pku2406使用kmp解o
(1)
1.5.2重复次数最多的连续重复子串(spoj687,pku3693)
给定一个字符串,求重复次数最多的连续重复子串。
算法分析:
穷举长度L的时间是n,每次计算的时间是n/L。
所以整个做法的时间复杂度是O(n/1+n/2+n/3+……+n/n)=O(nlogn)。
charc;
intmain()
{
inti,j,jj,k,n,now,ans,nn;
scanf("%d",&nn);
while(nn--)
{
scanf("%d",&n);
for(i=0;i{
scanf("\n%c",&c);
r[i]=c;
}
da(r,sa,n,128);
calheight(r,sa,n);
for(i=1;i<=n;i++)RMQ[i]=height[i];
initRMQ(n);
ans=0;
for(i=1;i{
for(j=0;j+i{
k=lcp(j,j+i);
now=k/i;
///////////////////////////////////////////////关键...
//还差i-k%i个字符可以匹配多一个周期
//把j向前移动i-k%i个字符,
//如果可以匹配出来一个周期
//now就+1
///////////////////////////////////////////////
jj=j-(i-k%i);
if(jj>=0)
if(lcp(jj,jj+i)>=(i-k%i))now++;
if(now>ans)ans=now;
}
}
printf("%d\n",ans+1);
}
return0;
}
1.5多个字符串
1.5.1不小于k个字符串中的最长子串(pku3294)
给定n个字符串,求出现在不小于k个字符串中的最长子串。
算法分析:
将n个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组。
然后二分答案,用和例3同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k个的原串中。
这个做法的时间复杂度为O(nlogn)。
1.5.2每个字符串至少出现两次且不重叠的最长子串(spoj220)
给