zhxfl字符串文档格式.docx
- 文档编号:22760176
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:22
- 大小:37.32KB
zhxfl字符串文档格式.docx
《zhxfl字符串文档格式.docx》由会员分享,可在线阅读,更多相关《zhxfl字符串文档格式.docx(22页珍藏版)》请在冰豆网上搜索。
}
voidda(int*r,int*sa,intn,intm)
inti,j,p,*x=wa,*y=wb,*t;
for(i=0;
i<
m;
i++)wh[i]=0;
n;
i++)wh[x[i]=r[i]]++;
for(i=1;
i++)wh[i]+=wh[i-1];
for(i=n-1;
i>
=0;
i--)sa[--wh[x[i]]]=i;
for(j=1,p=1;
p<
j*=2,m=p)
{
if(j>
=n)break;
for(p=0,i=n-j;
i++)y[p++]=i;
i++)if(sa[i]>
=j)y[p++]=sa[i]-j;
i++)wv[i]=x[y[i]];
i++)wh[wv[i]]++;
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;
i++)Rank[sa[i]]=i;
height[Rank[i++]]=k)
if(Rank[i]-1>
=0)
for(k?
k--:
0,j=sa[Rank[i]-1];
r[i+k]==r[j+k]&
i+k<
n&
j+k<
k++);
return;
intGet(intn,int*sa)
inti,ans=n-sa[0];
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;
=n;
mm[i]=((i&
(i-1))==0)?
mm[i-1]+1:
mm[i-1];
i++)best[0][i]=i;
=mm[n];
for(j=1;
j<
=n+1-(1<
<
i);
j++)
a=best[i-1][j];
b=best[i-1][j+(1<
(i-1))];
if(RMQ[a]<
RMQ[b])best[i][j]=a;
elsebest[i][j]=b;
intaskRMQ(inta,intb)
intt;
t=mm[b-a+1];
b-=(1<
t)-1;
a=best[t][a];
b=best[t][b];
returnRMQ[a]<
RMQ[b]?
a:
b;
intlcp(inta,intb)
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<
i++,j++)r[j]=tmp[i];
n=n+m+1;
da(r,sa,n,'
{'
);
calheight(r,sa,n);
intmax=0;
i++)if(height[i]>
max&
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];
inti,k,n,t,m;
%d"
k)&
k)
scanf("
%s"
s);
m=strlen(s);
s[m]='
s+m+1);
n=strlen(s);
i++)r[i]=s[i];
r[m]=129;
da(r,sa,n,130);
height[0]=0;
f[i]=sa[i]<
//a字符串f[i]=1;
height[i]-=k-1;
if(height[i]<
0)height[i]=0;
ans=0;
for(t=0;
t<
=1;
t++)//表示入栈的串
top=0,sum=0;
if(f[0]==t)
stack[top++]=0;
if(f[i]==t)//入栈
if(top==0)
else
if(f[i-1]==t)
stack[top++]=height[i];
sum+=height[i];
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;
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)
stack[top-1]=min(stack[top-1],height[i]);
ans+=sum;
%I64d\n"
ans);
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];
i++){
k)mmin=sa[i],mmax=sa[i];
else{
mmin=min(mmin,sa[i]);
mmax=max(mmax,sa[i]);
if(mmax-mmin>
=k)return1;
intx[maxn];
inti,n;
n)&
n)
i++)scanf("
x[i]);
if(n==1)
0\n"
continue;
n-1;
i++)r[i]=x[i+1]-x[i]+88;
n--;
da(r,sa,n,88+88);
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("
elseprintf("
ans+1);
13.3可重叠的k次最长重复子串pku3261
给定一个字符串,求至少出现k次的最长重复子串,这k个子串可以重叠。
这题的做法和上一题差不多,也是先二分答案,然后将后缀分成若干组。
不同的是,这里要判断的是有没有一个组的后缀个数不小于k。
如果有,那么存在k个相同的子串满足条件,否则不存在。
这个做法的时间复杂度为O(nlogn)。
boolGet(intk,intn,intt)
inti,num=1;
i++)
k)num=1;
num++;
if(num>
=t)return1;
inti,n,k;
%d%d"
n,&
k)!
r[i]);
da(r,sa,n,1000001);
intleft=0,right=n,ans=0;
Get(mid,n,k))
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)。
i++)ans+=n-sa[i]-height[i];
inti,n,k,T;
T);
while(T--)
s);
i++)r[i]=s[i]-'
A'
+1;
Get(n,sa));
1.4.2回文子串Timus1297
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];
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));
inti,n,j;
s)!
r[n]=59;
for(i=n+1,j=n-1;
2*n+1;
j--,i++)r[i]=s[j]-'
//倒过来接在后面
da(r,sa,n*2+1,60);
calheight(r,sa,n*2+1);
build(1,1,n*2);
intMax=0;
//回文为奇数
intfirst=0;
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;
//回文为偶数
intx=Rank[2*n-i];
inty=Rank[i+1];
t=t*2;
first=i-t/2+1;
for(i=first;
first+Max;
i++)printf("
%c"
s[i]);
printf("
\n"
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;
inti,j,jj,k,n,now,ans,nn;
nn);
while(nn--)
n);
\n%c"
c);
r[i]=c;
da(r,sa,n,128);
i++)RMQ[i]=height[i];
initRMQ(n);
i++)//长度为i的子串最多可以出现多少次
for(j=0;
j+i<
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>
if(lcp(jj,jj+i)>
=(i-k%i))now++;
if(now>
ans)ans=now;
1.5多个字符串
1.5.1不小于k个字符串中的最长子串(pku3294)
给定n个字符串,求出现在不小于k个字符串中的最长子串。
算法分析:
将n个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组。
然后二分答案,用和例3同样的方法将后缀分成若干组,判断每组的后缀是否出现在不小于k个的原串中。
1.5.2每个字符串至少出现两次且不重叠的最长子串(spoj220)
给
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- zhxfl 字符串
![提示](https://static.bdocx.com/images/bang_tan.gif)