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

题意:

给两个串\(A、B\),问你长度\(>=k\)的有几对公共子串

思路:

先想一个朴素算法:
\(B\)接在\(A\)后面,然后去跑后缀数组,得到\(height\)数组,那么直接\(rmq\)就能\(O(1)\)得到任意两个\(A\)\(B\)的LCP。如果\(LCP >= k\),那么这个串的贡献对数为\(LCP - k + 1\)。但是这样遍历显然超时。
那么我们可以用单调栈优化这个问题:
我们构建一个递增的单调栈,那么栈顶就是最大,每个栈里的元素为贡献值\(height\)的大小,那么显然,某一位置对后面的贡献为\(min(height[i])\),所以如果遇到入栈元素比栈顶大,说明栈顶的贡献值从此刻开始就要变小了,那么就去更新这个贡献。

参考:

POJ 3415 Common Substrings

代码:

#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<stack>
#include<ctime>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const ull seed = 11;
const int MOD = 1e9 + 7;
using namespace std;

int str[maxn];
int t1[maxn], t2[maxn], c[maxn];
int sa[maxn];
int rk[maxn];
int height[maxn];
bool cmp(int *r, int a, int b, int l){
    return r[a] == r[b] && r[a + l] == r[b + l];
}
void da(int *str, int n, int m){
    n++;
    int i, j, p, *x = t1, *y = t2;
    for(i = 0; i < m; i++) c[i] = 0;
    for(i = 0; i < n; i++) c[x[i] = str[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(j = 1; j <= n; j <<= 1){
        p = 0;
        for(i = n - j; i < n; i++) y[p++] = i;
        for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
        for(i = 0; i < m; i++) c[i] = 0;
        for(i = 0; i < n; i++) c[x[y[i]]]++;
        for(i = 1; i < m; i++) c[i] += c[i - 1];
        for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
        swap(x, y);
        p = 1; x[sa[0]] = 0;
        for(i = 1; i < n; i++)
            x[sa[i]] = cmp(y, sa[i - 1], sa[i], j)? p - 1 : p++;
        if(p >= n) break;
        m = p;
    }
    int k = 0;
    n--;
    for(i = 0; i <= n; i++) rk[sa[i]] = i;
    for(i = 0; i < n; i++){
        if(k) k--;
        j = sa[rk[i] - 1];
        while(str[i + k] == str[j + k]) k++;
        height[rk[i]] = k;
    }
}
char s[maxn];
ll instack[maxn], num[maxn];
int main(){
    int k;
    while(~scanf("%d", &k) && k){
        scanf("%s", s);
        int len = strlen(s);
        int cnt = 0;
        for(int i = 0; i < len; i++){
            str[cnt++] = s[i];
        }
        int pos = cnt;  //#
        str[cnt++] = '$';
        scanf("%s", s);
        len = strlen(s);
        for(int i = 0; i < len; i++){
            str[cnt++] = s[i];
        }
        str[cnt] = 0;
        da(str, cnt, 130);

        int top = 0;
        ll tot = 0, ans = 0;
        for(int i = 2; i <= cnt; i++){   //栈里塞A
            if(height[i] < k){
                top = 0, tot = 0;
            }
            else{
                int number = 0;
                if(sa[i - 1] < pos){    //A对后面B的贡献
                    tot += height[i] - k + 1;
                    number++;
                }
                while(top > 0 && instack[top] >= height[i]){
                    tot -= num[top] * (instack[top] - k + 1);
                    tot += num[top] * (height[i] - k + 1);
                    number += num[top];
                    --top;
                }
                instack[++top] = height[i];
                num[top] = number;

                if(sa[i] > pos) ans += tot;
            }
        }

        top = 0, tot = 0;
        for(int i = 2; i <= cnt; i++){   //栈里塞B
            if(height[i] < k){
                top = 0, tot = 0;
            }
            else{
                int number = 0;
                if(sa[i - 1] > pos){    //B对后面A的贡献
                    tot += height[i] - k + 1;
                    number++;
                }
                while(top > 0 && instack[top] >= height[i]){
                    tot -= num[top] * (instack[top] - k + 1);
                    tot += num[top] * (height[i] - k + 1);
                    number += num[top];
                    --top;
                }
                instack[++top] = height[i];
                num[top] = number;

                if(sa[i] < pos) ans += tot;
            }
        }
        printf("%lld\n", ans);

    }
    return 0;
}



posted @ 2019-07-30 17:08  KirinSB  阅读(157)  评论(0编辑  收藏  举报