Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 7291 | Accepted: 2417 |
Description
A substring of a string T is defined as:
Given two strings A, B and one integer K, we define S, a set of triples (i, j, k):
You are to give the value of |S| for specific A, B and K.
Input
The input file contains several blocks of data. For each block, the first line contains one integer K, followed by two lines containing strings A and B, respectively. The input file is ended by K=0.
1 ≤ |A|, |B| ≤ 105
1 ≤ K ≤ min{|A|, |B|}
Characters of A and B are all Latin letters.
Output
For each case, output an integer |S|.
Sample Input
2 aababaa abaabaa 1 xx xx 0
Sample Output
22 5
这道题需要对height数组分组后,用单调栈优化。对于LCP=L>K的前缀,对答案的贡献是L-K+1.即长度为K,K+1.....L的公共字串。对于每一组,栈里维护height值递增,这样保证了每个height的贡献量为height[i]-K+1,因为有定理LCP(i,j)=min(height[i+1],...height[j])如果在height[i]之后插入一个height[k]<height[i],那么height[i]就等于废了,他的价值仅为height[k]了,对于之后的j,LCP(i,j)起码要<=height[k],像这种递减的值插入栈前,需要将栈里>=插入值的变成插入值,然后将插入值的宽度增加。因为这当中为了降低复杂度,用了一个sum来维护当前栈里的后缀与将要入栈的后缀的公共字串的个数。因此实际上的{将栈里>=插入值的变成插入值}的操作,体现在sum上,是对sum减去一段height之差*宽度。
补充:因为rank=i的后缀能与其rank前后的后缀都可能产生公共子串,但此算法只维护其前面的,因此排在A串后缀后面的B串后缀,应该用扫描B串后缀来维护,因为B在A后,以B为参考系,A就在前,这就是为什么要分别扫A和B串的原因。
补充完这段,接着开始:比如我们正在扫A串,那么当B串入栈时,sum均会加上L-K+1,而之后当有height值小的要入栈,此时可以是A串的后缀,他虽然不增加sum的值,但它也起到了阻断作用,它是之前栈里height值>=它的值变废了,因为后面的后缀与栈里算LCP值时,起码要<=这个值,由于栈里原先的元素都加了L-K+1,假设现在入栈的height值为T(T<L),那么它对答案的贡献是T-K+1,它使原先栈里大于等于它废成T,贡献量变为T-K+1,相当于减少了L-K+1-(T-K+1)=L-K(高度差),因为之前入栈时已加在sum上,此时就要从sum中减去了,当然不能忘了乘宽度,因为这些height都会合并,他代表的是一系列height相同的元素,是有数目的。
总之,这道题个人感觉方法很棒!我也是理解了好久,可能上面解释的不太到位吧,还需要各位更深入的研究。
代码:
#include<cstdio>
#include<iostream>
#define ll long long
#define Maxn 200010
using namespace std;
int r[Maxn],sa[Maxn],rk[Maxn],height[Maxn];
int wa[Maxn],wb[Maxn],rs[Maxn],wv[Maxn];
int cmp(int *r,int a,int b,int l){
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int n,int m){
int i,j,p,*x=wa,*y=wb;
for(i=0;i<m;i++) rs[i]=0;
for(i=0;i<n;i++) rs[x[i]=r[i]]++;
for(i=1;i<m;i++) rs[i]+=rs[i-1];
for(i=n-1;i>=0;i--) sa[--rs[x[i]]]=i;
for(j=1,p=1;p<n;j<<=1,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<m;i++) rs[i]=0;
for(i=0;i<n;i++) rs[wv[i]=x[y[i]]]++;
for(i=1;i<m;i++) rs[i]+=rs[i-1];
for(i=n-1;i>=0;i--) sa[--rs[wv[i]]]=y[i];
swap(x,y);
for(p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
void calheight(int n){
int i,j,k=0;
for(int i=1;i<n;i++) rk[sa[i]]=i;
for(int i=1;i<n;height[rk[i++]]=k){
if(k) k--;
for(j=sa[rk[i]-1];r[i+k]==r[j+k];k++);
}
}
char s[Maxn];
int st[Maxn],len[Maxn];
int main()
{
int n,i,k,la,lb;
while(scanf("%d",&k),k){
scanf("%s",s+1);
for(i=1;s[i];i++)
r[i]=s[i];
r[n=i]='#';
la=n-1;
scanf("%s",s+1);
for(i=1;s[i];i++)
r[++n]=s[i];
lb=n-la-1;
r[0]=r[++n]=0;
da(n,125);
calheight(n);
int top;
ll ans=0,sum;
for(int i=1;i<n;i++){
if(height[i]<k) top=-1,sum=0;
else{
int width=0;
while(top!=-1&&st[top]>height[i]){
sum-=len[top]*(st[top]-height[i]);
width+=len[top--];
}
if(sa[i-1]<=la) width+=1,sum+=height[i]-k+1;
st[++top]=height[i];
len[top]=width;
if(sa[i]>la) ans+=sum;
}
}
for(int i=1;i<n;i++){
if(height[i]<k) top=-1,sum=0;
else{
int width=0;
while(top!=-1&&st[top]>height[i]){
sum-=(ll)len[top]*(st[top]-height[i]);
width+=len[top--];
}
if(sa[i-1]>la) width+=1,sum+=height[i]-k+1;
st[++top]=height[i];
len[top]=width;
if(sa[i]<=la) ans+=sum;
}
}
printf("%lld\n",ans);
}
return 0;
}