POJ 3415 Common Substrings(后缀数组+单调栈)

题意: 求长度不小于k的公共子串个数
·
·
·
若不考虑时间复杂度,这题就是对两个串的每个后缀两两求LCP,取LCP不小于k的累加LCP-k+1即为答案
在这种思路的基础上,用单调栈优化即可(栈顶元素最大)
后缀数组求出height后,对height数组分组,即连续的height的值不小于k的为一组,对每组进行独立的操作
先考虑用B的后缀匹配A的后缀
按顺序遍历每组height,若遇到A的后缀,压入栈中,若遇到B的后缀,统计与前面所有出现过的A的后缀的答案,这个答案在更新栈的过程中应该一起处理,并看B能不能更新栈(B的后缀不能增加栈内元素,只能更新)
最后在用A的后缀匹配B的后缀即可

#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int maxn = 200010;
int n;
int sa[maxn], x[maxn], c[maxn], y[maxn], height[maxn];
char s[maxn], s1[maxn];
void SA()
{
    int m = 128;
    for (int i = 0; i <= m; i++)
        c[i] = 0;
    for (int i = 1; i <= n; i++)
        c[x[i] = s[i]]++;
    for (int i = 1; i <= m; i++)
        c[i] += c[i - 1];
    for (int i = n; i >= 1; i--)
        sa[c[x[i]]--] = i;

    for (int k = 1; k <= n; k <<= 1)
    {
        int p = 0;
        for (int i = 0; i <= m; i++)
            y[i] = 0;
        for (int i = n - k + 1; i <= n; i++)
            y[++p] = i;
        for (int i = 1; i <= n; i++)
            if (sa[i] > k)
                y[++p] = sa[i] - k;

        for (int i = 0; i <= m; i++)
            c[i] = 0;
        for (int i = 1; i <= n; i++)
            c[x[y[i]]]++;
        for (int i = 1; i <= m; i++)
            c[i] += c[i - 1];
        for (int i = n; i >= 1; i--)
            sa[c[x[y[i]]]--] = y[i];

        swap(x, y);
        x[sa[1]] = 1;
        p = 1;
        for (int i = 2; i <= n; ++i)
            x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? p : ++p;
        if (p >= n)
            break;
        m = p;
    }
}

void get_height()
{
    int k = 0;
    for (int i = 1; i <= n; ++i)
    {
        if (x[i] == 1)
            continue;
        if (k)
            --k;
        int j = sa[x[i] - 1];
        while (j + k <= n && i + k <= n && s[i + k] == s[j + k])
            ++k;
        height[x[i]] = k;
    }
}
ll sta[maxn], cnt[maxn];
int main()
{
    int t;
    while (scanf("%d", &t) && t)
    {
        scanf("%s%s", s + 1, s1 + 1);
        int len = strlen(s + 1);
        s[len + 1] = '*';
        s[len + 2] = '\0';
        strcat(s + 1, s1 + 1);
        n = strlen(s + 1);
        SA();
        get_height();
        ll ans = 0, sum = 0;
        int tp = 0;
        for (int i = 1; i <= n; i++) //B match A
        {
            if (sa[i] <= len)
            {
                ll tot = 0;
                while (tp != 0 && height[sta[tp]] >= height[i + 1])
                {
                    sum -= cnt[tp] * (height[sta[tp]] - t + 1);
                    sum += cnt[tp] * (height[i + 1] - t + 1);
                    tot += cnt[tp];
                    cnt[tp] = 0;
                    --tp;
                }
                sta[++tp] = i + 1;
                cnt[tp] = tot + 1;
                sum += height[i + 1] - t + 1;
            }
            else if (sa[i] > len + 1)
            {
                ans += sum;
                if (tp != 0 && height[sta[tp]] > height[i + 1])
                {
                    ll tot = 0;
                    while (tp != 0 && height[sta[tp]] > height[i + 1])
                    {
                        sum -= cnt[tp] * (height[sta[tp]] - t + 1);
                        sum += cnt[tp] * (height[i + 1] - t + 1);
                        tot += cnt[tp];
                        cnt[tp] = 0;
                        --tp;
                    }
                    sta[++tp] = i + 1;
                    cnt[tp] = tot ;
                }
            }
            if(height[i+1]<t)
            {
                tp=0;sum=0;
            }
        }
        tp = 0, sum = 0;
        for (int i = 1; i <= n; i++) //A match B
        {
            if (sa[i] > len + 1)
            {
                ll tot = 0;
                while (tp != 0 && height[sta[tp]] >= height[i + 1])
                {
                    sum -= cnt[tp] * (height[sta[tp]] - t + 1);
                    sum += cnt[tp] * (height[i + 1] - t + 1);
                    tot += cnt[tp];
                    cnt[tp] = 0;
                    --tp;
                }
                sta[++tp] = i + 1;
                cnt[tp] = tot + 1;
                sum += height[i + 1] - t + 1;
            }
            else if (sa[i] <= len)
            {
                ans += sum;
                if (tp != 0 && height[sta[tp]] > height[i + 1])
                {
                    ll tot = 0;
                    while (tp != 0 && height[sta[tp]] > height[i + 1])
                    {
                        sum -= cnt[tp] * (height[sta[tp]] - t + 1);
                        sum += cnt[tp] * (height[i + 1] - t + 1);
                        tot += cnt[tp];
                        cnt[tp] = 0;
                        --tp;
                    }
                    sta[++tp] = i + 1;
                    cnt[tp] = tot;
                }
            }
            if(height[i+1]<t)
            {
                tp=0;sum=0;
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2019-11-12 21:13  Zeronera  阅读(74)  评论(0)    收藏  举报