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
题意:给出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 int int 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; }