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=k的字串数,两个lcp为K的后缀,>=k的字串为K-k+1。
#include
#include
#include
#include
#include
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;
}