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);
    }
}

猜你喜欢

转载自www.cnblogs.com/LORDXX/p/11979916.html