kuangbin后缀数组 - I题 POJ3415 单调栈解法
Posted lordxx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kuangbin后缀数组 - I题 POJ3415 单调栈解法相关的知识,希望对你有一定的参考价值。
题意,给出两个字符串,给一个k,求出满足a的字串与b的子串完全一样的长度>=k的所有情况的数量。
翻译一下题意就是求所有后缀中,lcp>=k的即可。
我们拼接两个字符串,中间用不出现的字符隔开即可。
然后就是如何求解的问题。
我们知道lcp(i,j)=min(height[i+1],....,height[j])
所以我们进行da()后,getheight()获得数组后,就可以利用单调栈的性质来求解。可以类比最大子矩阵的求解方法。
假如我们排序出来的height数组为:2 ,3,3,4,3,1。相当于七个后缀求长度。
令七个后缀分别为 A,B,B,A,B,B,B。(所属)
先看height=2,这时lcp=2,等价为B1的前缀有两个与A1相等,如果k=1,
那么贡献就为2-1+1=2。
可以假设B1=aabb,A1=aacc,就有a,aa两个满足题意。但这里其实还有第二个a满足题意,那我们会在哪里统计呢?会在以单独a开头的那个后缀统计。所以不会漏记。
所以对于B1,B2来说,贡献都为2-1+1,而对于B3来说,有插入了一个a,贡献为4,那么B3得到的贡献为2-1+1+4-1+1。还与前面的A1匹配。
那么用单调栈,我们先以b为参照匹配。即单调栈存B的高度,且单调向上。
每一次遇到了一个矮的高度,弹栈。注意弹栈的时候需要重新计算的东西。一个是如果这个栈记录的高度对应的那个B是刚好与A相接的,例如这里的B1,B3。
那么贡献就会变少,相当于后面与这个a字符串匹配的数量整体会减少。
最后一个问题,假如我们弹栈弹到与A紧邻的B的时候,是不是只要减去贡献就行了。肯定是有问题的,因为这个高度可能是已经被修改过的了,意味着,可能这个里面不止有一个A,所以需要记录num即数量,紧邻A的B,num记为1,在弹栈的时候,加上所有弹出栈的num就是更改的A的数量。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
char ch = getchar(); ll x = 0, f = 1;
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 1e5 + 10;
char a[N],b[N];
char s[2*N];
int k;
int n;
int rk[2 * N], sa[2 * N], t[2 * N], t2[2 * N], c[2 * N], height[2 * N];
int st[2 * N];
ll cnt[2 * N];
void da()
{
int *x = t, *y = t2;
int m = 130;
up(i, 0, m)c[i] = 0;
up(i, 0, n)c[x[i] = s[i]]++;
up(i, 1, m)c[i] += c[i - 1];
dwd(i, n - 1, 0)sa[--c[x[i]]] = i;
for (int k = 1; k <= n; k <<= 1)
{
int p = 0;
up(i, n - k, n)y[p++] = i;
up(i, 0, n)if (sa[i] >= k)y[p++] = sa[i] - k;
up(i, 0, m)c[i] = 0;
up(i, 0, n)c[x[y[i]]]++;
up(i, 1, m)c[i] += c[i - 1];
dwd(i, n - 1, 0)sa[--c[x[y[i]]]] = y[i];
swap(x, y);
x[sa[0]] = 0;
p = 1;
up(i, 1, n)
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;
//cout << ")" << endl;
}
}
void getheight()
{
up(i, 0, n)rk[sa[i]] = i;
int j = 0;
int k = 0;
up(i, 0, n)
{
if (k)k--;
j = sa[rk[i] - 1];
while (s[i + k] == s[j + k])k++;
height[rk[i]] = k;
}
}
void test()
{
cout << "suffix:" << endl;
up(i, 0, n)
{
//cout << "sa" << sa[i] << endl;
up(j, sa[i], n)cout << s[j] << " ";
cout << endl;
cout << height[i] << endl;
}
}
void work(int len1,int len2)
{
int top = 0;
ll anstemp = 0;
ll ans = 0;
up(i, 0, n)
{
if (height[i] < k)top = 0, anstemp = 0;
else {
ll num = 0;
if (sa[i - 1] < len1)anstemp += height[i] - k + 1,num=1;
while (top&&st[top] >= height[i])
{
anstemp -= 1ll*cnt[top] * (st[top]-height[i]);
num += cnt[top];
top--;
}
st[++top] = height[i];
cnt[top] = num;
if(sa[i]>len1)
ans += anstemp;
}
}
up(i, 0, n)
{
if (height[i] < k)top = 0, anstemp = 0;
else {
ll num = 0;
if (sa[i - 1] > len1)anstemp += height[i] - k + 1, num=1;
while (top&&st[top] >= height[i])
{
anstemp -= 1ll*cnt[top] * (st[top] - height[i]);
num += cnt[top];
top--;
}
st[++top] = height[i];
cnt[top] = num;
if(sa[i]<len1)
ans += anstemp;
}
}
cout << ans << endl;
}
int main()
{
while (scanf("%d",&k) && k)
{
scanf("%s",a);
scanf("%s",b);
int len1 = strlen(a);
int len2 = strlen(b);
up(i, 0, len1)s[i] = a[i];
s[len1] = '#';
up(i, 0, len2)s[i + len1+1] = b[i];
n = len1 + len2 + 1;
// up(i, 0, n)cout << s[i] << " ";
da();
getheight();
// test();
work(len1,len2);
}
}
以上是关于kuangbin后缀数组 - I题 POJ3415 单调栈解法的主要内容,如果未能解决你的问题,请参考以下文章