poj 3415 Common Substrings

Common Substrings
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 7291   Accepted: 2417

Description

A substring of a string T is defined as:

T( ik)= TiTi +1... Ti+k -1, 1≤ ii+k-1≤| T|.

Given two strings AB and one integer K, we define S, a set of triples (ijk):

S = {( ijk) |  kKA( ik)= B( jk)}.

You are to give the value of |S| for specific AB 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]=插入值的变成插入值,然后将插入值的宽度增加。因为这当中为了降低复杂度,用了一个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(TT-K+1,相当于减少了L-K+1-(T-K+1)=L-K(高度差),因为之前入栈时已加在sum上,此时就要从sum中减去了,当然不能忘了乘宽度,因为这些height都会合并,他代表的是一系列height相同的元素,是有数目的。

总之,这道题个人感觉方法很棒!我也是理解了好久,可能上面解释的不太到位吧,还需要各位更深入的研究。

代码:

#include
#include
#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=0;i--) sa[--rs[x[i]]]=i;
    for(j=1,p=1;p=j) y[p++]=sa[i]-j;
        for(i=0;i=0;i--) sa[--rs[wv[i]]]=y[i];
        swap(x,y);
        for(p=1,x[sa[0]]=0,i=1;iheight[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;iheight[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;
}

你可能感兴趣的:(后缀数组学习)