您的位置:首页 > 其它

poj 3415 Common Substrings(后缀数组+单调栈+dp)

2017-09-26 16:20 260 查看
题目链接:点击打开链接
博主是看了后缀数组那篇论文后,然后做了这道题练手。
论文原文:基本思路是计算A
的所有后缀和B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k
的部分全部加起来。先将两个字符串连起来,中间用一个没有出现过的字符隔开。按height
值分组后,接下来的工作便是快速的统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个B
的后缀就统计与前面的A
的后缀能产生多少个长度不小于k 的公共子串,这里A
的后缀需要用一个单调的栈来高效的维护。然后对A 也这样做一次。具体的细节留给读者思考。
然后在网上搜了好几篇解题报告,都只提到了用单调栈来维护,而都没有讲清楚具体思路。感觉看得模模糊糊,很是不详细,花了不少时间在这个题目上,遂写下了这篇博文,希望大家能给大家带来帮助(本人语文水平不太好,轻喷)。
首先对于字符串的两个后缀i,j的lcp(最长公共子串),长度为rank[i]+1~rank[j]区间内height[]的最小值(不妨设rank[i]<rank[j])。按照height分组后,需要统计每一个新的后缀与前面的后缀带来的新的公共前缀,这里视为贡献度,这里如果是o(n^2)的复杂度的话,明显超时。所以想到统计新的后缀所带来的贡献的时候,能不能用前一个后缀所做的贡献来加快时间。我们用一个单调递增的栈来维护数据。
对于每一个新的后缀,如果heigh值大于栈顶元素:
例如:
(这里Sa[i]表示的字符串是什么不重要,重要的是动态规划思维过程~~~~)
Sa[1]:ab
Sa[2]:abc
Sa[3]:abcd
Sa[4]:abcde
若此时算到Sa[4]这个新的后缀和前面所有后缀(Sa[3],Sa[2],Sa[1])能带来多大贡献,首先对于(Sa[1],Sa[2])来说,因为height[4]>height[3](单调栈栈顶元素),根据lcp的计算方法,所以Sa[3]能和(Sa[1],Sa[2])产生的贡献值就等同于Sa[3]和(Sa[1],Sa[2])所产生的贡献值,Sa[4]能和Sa[3]产生的贡献值为(height[4]-k+1),所以Sa[4]能产生的新的贡献值=(Sa[3]产生的贡献值+height[4]-k+1)。
如果height值大于栈顶元素值:
Sa[1]:a
Sa[2]:ab
Sa[3]:abc
Sa[4]:abcd
Sa[5]:abcde
Sa[6]:abcdef
Sa[7]:abc
Sa[8]:ab
若此时计算到Sa[7],height[7]<栈顶元素值height[6],若此时还按照上面的求解方法,明显多算了Sa[5],Sa[6]后面的后缀e,ef,所以这里要减去Stacksize[i]*(Stack[top]-height[i])这个多算的后缀,Stacksize数组的含义马上会说到,它是表示这一段Stack[i]这一段压缩了多少个数。因为height[7]<栈顶值,但我们要维护一个单调递增的栈,又因为lcp是这段区间的最小值,所以后面和它前面的lcp最大长度不会超过height[7],所以以后开始的后缀同这段区间的lcp不会超过height[7],Stacksize[top]表示严格递增的(height[i]<=3&&height[i]>Stack[top-1])区间含有几个数。后面按照这种规律依次递推就能出结果了~~~其实这就是一个统计上的Dp吗~~~~~
然后来说,这个题目就不难了,先用一个独特的字符连接两个字符串,求出后缀数组后分情况统计,先求出B的后缀有多少个和A的公共前缀,再统计A的后缀有多少个和B的公共前缀~~~~~~结果就出来了~~~~~~~
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long llt;
const int maxn=200010;
int wa[maxn],wb[maxn],wsf[maxn],wv[maxn],sa[maxn];
int ranks[maxn],height[maxn],s[maxn];
char s1[maxn],s2[maxn],str[maxn];
int cmp(int *r,int a,int b,int k)
{
return r[a]==r[b]&&r[a+k]==r[b+k];
}
void da(char *r,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) wsf[i]=0;
for(i=0;i<n;i++) wsf[x[i]=r[i]]++;
for(i=1;i<m;i++) wsf[i]+=wsf[i-1];
for(i=n-1;i>=0;i--) sa[--wsf[x[i]]]=i;
p=1;
j=1;
for(;p<n;j*=2,m=p)
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) wsf[i]=0;
for(i=0;i<n;i++) wsf[wv[i]]++;
for(i=1;i<m;i++) wsf[i]+=wsf[i-1];
for(i=n-1;i>=0;i--) sa[--wsf[wv[i]]]=y[i];
t=x;
x=y;
y=t;
x[sa[0]]=0;
for(p=1,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++;
}
}
void calheight(char *r,int n)
{
int i,j,k=0;
for(i=0;i<n;i++) ranks[sa[i]]=i;
for(i=0;i<n-1;i++)
{
if(k) k--;
j=sa[ranks[i]-1];
while(r[i+k]==r[j+k]) k++;
height[ranks[i]]=k;
}
}
int Stack[maxn],Stacksize[maxn];
llt tot,top;
int main()
{
int k;
while(scanf("%d%s%s",&k,s1,s2)&&k)
{
int L1=strlen(s1),L2=strlen(s2);
str[0]='\0';
strcat(str,s1);
str[L1]=1;
str[L1+1]='\0';
strcat(str,s2);
int n=L1+L2+1;
da(str,n+1,128);
calheight(str,n+1);
tot=top=0;
llt sum=0;
for(int i=2; i<=n; i++)
{
if(height[i]<k) top=tot=0;
else
{
int cnt=0;
if(sa[i-1]<L1) cnt++,tot+=height[i]-k+1;
while(top>0&&height[i]<=Stack[top-1])
{
top--;
tot-=Stacksize[top]*(Stack[top]-height[i]);
cnt+=Stacksize[top];
}
Stack[top]=height[i];
Stacksize[top++]=cnt;
if(sa[i]>L1) sum+=tot;
}
}
tot=top=0;
for(int i=2; i<=n; i++)
{
if(height[i]<k) top=tot=0;
else
{
int cnt=0;
if(sa[i-1]>L1) cnt++,tot+=height[i]-k+1;
while(top>0&&height[i]<=Stack[top-1])
{
top--;
tot-=Stacksize[top]*(Stack[top]-height[i]);
cnt+=Stacksize[top];
}
Stack[top]=height[i];
Stacksize[top++]=cnt;
if(sa[i]<L1) sum+=tot;
}
}
printf("%lld\n",sum);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: