POJ3415--Common Substrings(后缀数组+单调栈优化)

来源:互联网 发布:网络加速器vpn 编辑:程序博客网 时间:2024/04/29 06:16

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

2aababaaabaabaa1xxxx0

Sample Output

225
题意:给出K和2个串,求这2个串中长度大于等于K的公共字串的个数
思路:很容易看出枚举长度肯定会超时!我想了很久,也没想出来。然后就找题解啊~~结果得用单调栈优化
     先说下大方向,将两个串中间用个没出现过的字符隔开,后面添加一个最小字符。构建后缀数组。
     然后扫描2次,第一扫描算出每个A串后缀与前面的B串后缀的>=k长度的公共字串数
     第二次扫描求出每个B串后缀与前面的A串后缀的>=k长度的公共子串数。
     具体扫描就是,维护一个单调栈(lcp)。比如现在是第一轮扫描。遇到一个新串就得更新之前的lcp。我们首先要清楚
     sa[i]和sa[j]的lcp是夹在其中的这段height[]中的最小值。
     看到这里就相对好想了,我们用一个栈,从栈顶到栈底的lcp依次减小。每扫描一个新的height[],如果栈顶的
     h>=height[i],那就得将其h改为height[i],个数统计起来。直到栈顶的h<height[].然后再将height[i]和个数     (用结构体)入栈。。至于计算>=k的字串数,两个lcp为K的后缀,>=k的字串为K-k+1。
#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <vector>using namespace std;#define maxn 240080#define inf 0x3f3f3f3f#define LL long long intint str[maxn],vis[maxn];char s[maxn];int sa[maxn],t[maxn],t2[maxn],c[maxn];int height[maxn],Rank[maxn];int len1,len2;inline int max(int a,int b){return a>b?a:b;}/*用SA模板注意在最后添加一个比所有字符都小的字符。key[n] = 0;build_sa(key,n+1,m);getHeight(key,n+1);显然sa[0] 就是最后那个位置。。。height[i] 表示 sa[i] 和 sa[i-1] 的最长公共前缀。。*/void build_sa(int * s,int n,int m){int i,*x = t,*y = t2;for(i = 0;i < m;i++)c[i] = 0;for(i = 0;i < n;i++)c[ x[i] = s[i] ]++;for(i = 1;i < m;i++)c[i] += c[i-1];for(i = n-1;i >= 0;i--)sa[--c[x[i]]] = i;for(int k = 1;k <= n;k <<= 1){int p = 0;for(i = n - k;i < n;i++)y[p++] = i;for(i = 0;i < n;i++)if(sa[i] >= k)y[p++] = sa[i] - k;for(i = 0;i < m;i++)c[i] = 0;for(i = 0;i < n;i++)c[ x[y[i]] ]++;for(i = 0;i < m;i++)c[i] += c[i-1];for(i = n-1;i >= 0;i--)sa[--c[x[y[i]]]] = y[i];//根据sa和y数组计算新的数y组swap(x,y);p = 1;x[sa[0]] = 0;for(i = 1;i < n;i++)x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p-1:p++;if(p >= n)break;m = p;}}void getHeight(int * s,int n){int i,j,k = 0;for(i = 0;i < n;i++)Rank[sa[i]] = i;for(i = 0;i < n;i++){if(k) k--;int j = sa[Rank[i]-1];while(s[i+k] == s[j+k])k++;height[Rank[i]] = k;}}struct Item{LL h,num;Item(){}Item(LL hh,LL nn){h = hh;num = nn;}}S[maxn];LL query(int n,int a,int k){LL ans = 0,sum = 0;int first = maxn-1,rear = maxn-1;for(int i = 1;i < n;i++){if(height[i] < k){first = rear;sum = 0;continue;}LL num = 0;while(first < rear && S[first].h >= height[i]){num += S[first].num;sum -= (S[first].h - k + 1)*S[first].num;first++;}S[--first] = Item(height[i],num);sum += (height[i] - k + 1)*num;if(vis[sa[i-1]] && vis[sa[i-1]]!= a){sum += height[i] - k + 1;S[first].num++;}if(vis[sa[i]] == a)ans += sum;//统计完后就得更新}return ans;}int main(){//freopen("in.txt","r",stdin);int k;while(scanf("%d",&k)!=EOF && k){scanf("%s",s);len1 = strlen(s);for(int i = 0;i < len1;i++){if(s[i] < 'a')str[i] = s[i] - 'A' + 27;else str[i] = s[i] - 'a' + 1;vis[i] = 1;}vis[len1] = 0;str[len1++] = 54;scanf("%s",s);len2 = strlen(s);for(int i = 0;i < len2;i++){if(s[i] < 'a')str[len1+i] = s[i] - 'A' + 27;else str[len1+i] = s[i] - 'a' + 1;vis[len1+i] = 2;}vis[len1+len2] = 0;str[len1+len2] = 0;build_sa(str,len1+len2+1,55);getHeight(str,len1+len2+1);LL ans = 0;ans += query(len1+len2+1,1,k);ans += query(len1+len2+1,2,k);printf("%lld\n",ans);}return 0;}


0 0
原创粉丝点击