zhxfl字符串.docx

上传人:b****7 文档编号:9624163 上传时间:2023-02-05 格式:DOCX 页数:22 大小:37.32KB
下载 相关 举报
zhxfl字符串.docx_第1页
第1页 / 共22页
zhxfl字符串.docx_第2页
第2页 / 共22页
zhxfl字符串.docx_第3页
第3页 / 共22页
zhxfl字符串.docx_第4页
第4页 / 共22页
zhxfl字符串.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

zhxfl字符串.docx

《zhxfl字符串.docx》由会员分享,可在线阅读,更多相关《zhxfl字符串.docx(22页珍藏版)》请在冰豆网上搜索。

zhxfl字符串.docx

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;i

for(i=0;i

for(i=1;i

for(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;i

for(i=0;i=j)y[p++]=sa[i]-j;

for(i=0;i

for(i=0;i

for(i=0;i

for(i=1;i

for(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;i

x[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;i

for(i=0;i

if(Rank[i]-1>=0)

for(k?

k--:

0,j=sa[Rank[i]-1];r[i+k]==r[j+k]&&i+k

return;

}

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;i

n=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;i

r[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;i

if(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;i

if(n==1)

{

printf("0\n");

continue;

}

for(i=0;i

n--;

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;i

da(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;i

returnans;

}

chars[maxn];

intmain()

{

inti,n,k,T;

scanf("%d",&T);

while(T--)

{

scanf("%s",&s);

n=strlen(s);

for(i=0;i

da(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;i

r[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)

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 职业教育 > 中职中专

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1