zoukankan      html  css  js  c++  java
  • 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
    ", ans);
    
        }
        return 0;
    }
    
    
    
    
  • 相关阅读:
    二进制位运算
    Leetcode 373. Find K Pairs with Smallest Sums
    priority_queue的用法
    Leetcode 110. Balanced Binary Tree
    Leetcode 104. Maximum Depth of Binary Tree
    Leetcode 111. Minimum Depth of Binary Tree
    Leetcode 64. Minimum Path Sum
    Leetcode 63. Unique Paths II
    经典的递归练习
    案例:java中的基本排序
  • 原文地址:https://www.cnblogs.com/KirinSB/p/11271291.html
Copyright © 2011-2022 走看看