Common Substrings

血淋淋的教训!!!!!!!

这一题我觉得出的很好。后缀数组+单调栈。

我们很容易想到两个字符串通过间隔符相连
height分组筛选出来一堆互相最长公共前缀大于等于K的后缀
但是如何统计他们之间的贡献却成为了一个难事情。
这里我们使用的技巧叫做 单调栈!!!

其实好久之前我就遇到单调栈了,但是当时没有钻研。。。。。。

分组之后,我们统计每一个区间中属于s2的后缀与他前面属于s1的后缀所造成的贡献
这样再扫一遍统计属于s1的后缀与他前面属于s2的后缀所造成的贡献。那就是答案了。

这两个问题是对称的,我们来看第一个问题。
假设我们对于s2的后缀i要统计他前面的s1的后缀对他的贡献

这是一个怎样的过程?
我们从i往前找,一路统计最小值,遇到s1的后缀就 ans+=(min-K+1)
对不对。
我们会发现,一个最小值可以对应多个s1的后缀。
也就是说,直到遇到下一个比我小的,我min始终不变,在此路上,遇到的s!的后缀的贡献全部都是min-K+1

那么我们可以使用单调栈,从上往下扫。
我们向栈中压一个二元组(min,cnt)
指 min(height)为min的s1的后缀有cnt个

每当我们扫描到下一个时看一下他的heigh[j]
然后从栈顶开始看,如果栈顶元素的min<height[j]记录下他的cnt然后把他pop掉
然后减去这个区间队后面s2的后缀做的贡献(min-K+1)cnt
就这样直到,栈顶元素的min>height[j]然后在压入height和我们一路记录下来的cnt之和
并加上这个区间中的s1的后缀队后面的s2的后缀做的贡献(height[j]-K+!)
cnt
这其实就是,到j了,之前的元素如果其min比height[j]自然就应该被替换成height[j]
很是巧妙,我真的佩服发明出单调栈和单调队列的人们!!!!!!!!!
我们会发现一进一出时间O(n)
真牛!!!!!!
真的是应该好好理解。
我们之所以维护一个单调递增栈而不是一个单调递减栈,其原因就是:
在从上往下遍历的过程中。后面出现的height[]小于之前的height[]不会影响
但是如果小于的话就会影响。反应在上述操作中的就是我们需要一路pop栈顶,直到栈顶元素的height[]小于当前height[]
即前面的不在受影响!!!!
真牛!!!!!!!!!!!简单但是巧妙

而我在哪里被坑了呢?
我是在求后缀数组时被坑的!!!
我是无论是字符还是数字都统统转化为数字求的后缀数组。
在多组数据时,竟然在求解height数组时会因为前面的数据而导致当前数据求解错误!!!!!
原因是在比对r过程中,前面的数据残留会导致错误。
在我后缀数组的模板中r中似不可以出现0的,所以为了避免前面数据残留在承德影响,我们需要a[n]=0
坑死人了!!!!!!WA了一天!!!呜呜呜呜呜。。。。。。还好不是正式比赛!!!!!:)

代码如下

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
const int max_n = 2e5 + 100;
int a[max_n];
int ranks[max_n], SA[max_n], height[max_n];
int wa[max_n], wb[max_n], wvarr[max_n], wsarr[max_n];
inline int cmp(int* r, int a, int b, int l) {
    return r[a] == r[b] && r[a + l] == r[b + l];
}
inline void get_sa(int* r, int* sa, int n, int m) {
    ++n;
    int i, j, p, * x = wa, * y = wb, * t;
    for (i = 0; i < m; ++i) wsarr[i] = 0;
    for (i = 0; i < n; ++i) wsarr[x[i] = r[i]]++;
    for (i = 1; i < m; ++i) wsarr[i] += wsarr[i - 1];
    for (i = n - 1; i >= 0; --i) sa[--wsarr[x[i]]] = i;
    for (j = 1, p = 1; p < n; j <<= 1, m = p) {
        for (p = 0, 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 < n; ++i) wvarr[i] = x[y[i]];
        for (i = 0; i < m; ++i) wsarr[i] = 0;
        for (i = 0; i < n; ++i) wsarr[wvarr[i]]++;
        for (i = 1; i < m; ++i) wsarr[i] += wsarr[i - 1];
        for (i = n - 1; i >= 0; --i) sa[--wsarr[wvarr[i]]] = y[i];
        for (t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
    }
}
void get_height(int* r, int* sa, int n) {
    int i, j, k = 0;
    for (i = 0; i <= n; ++i) ranks[sa[i]] = i;
    for (i = 0; i < n; height[ranks[i++]] = k)
        for (k ? k-- : 0, j = sa[ranks[i] - 1]; r[i + k] == r[j + k]; k++);
    return;
}
int K;
char s1[max_n], s2[max_n];
int main() {
    while (scanf("%d", &K)) {
        if (K == 0)break;
        scanf("%s\n%s", &s1, &s2);
        int n1 = strlen(s1);
        int n2 = strlen(s2);
        for (int i = 0;i < n1;++i)
            a[i] = s1[i];
        a[n1] = 290;
        for (int i = n1 + 1;i < n1 + n2 + 1;++i)
            a[i] = s2[i - n1 - 1];
        a[n1 + n2 + 1] = 0;
        get_sa(a, SA, n1 + n2 + 1, 300);
        get_height(a, SA, n1 + n2 + 1);
        ll ans = 0;ll sum = 0;
        stack<pll> st;
        for (int i = 2;i <= n1 + n2 + 1;++i) {
            ll cnt = 0;
            while (!st.empty() && st.top().first >= height[i]) {
                cnt += st.top().second;
                sum -= (st.top().first - K + 1) * st.top().second;
                st.pop();
            }if (height[i] < K) {
                sum = 0;
                continue;
            }cnt += (SA[i - 1] < n1);
            if (cnt != 0)st.push(pll(height[i], cnt));
            sum += ((ll)height[i] - K + 1) * cnt;
            if (SA[i] > n1)ans += sum;
        }sum = 0;
        while (!st.empty())st.pop();
        for (int i = 2;i <= n1 + n2 + 1;++i) {
            ll cnt = 0;
            while (!st.empty() && st.top().first >= height[i]) {
                cnt += st.top().second;
                sum -= (st.top().first - K + 1) * st.top().second;
                st.pop();
            }if (height[i] < K) {
                sum = 0;
                continue;
            }cnt += (SA[i - 1] > n1);
            if (cnt != 0)st.push(pll(height[i], cnt));
            sum += ((ll)height[i] - K + 1) * cnt;
            if (SA[i] < n1)ans += sum;
        }printf("%lld\n", ans);
    }
}

题单:https://vjudge.net/article/371

全部评论

相关推荐

11-28 17:48
中山大学 C++
点赞 评论 收藏
分享
斑驳不同:还为啥暴躁 假的不骂你骂谁啊
点赞 评论 收藏
分享
找不到工作死了算了:没事的,雨英,hr肯主动告知结果已经超越大部分hr了
点赞 评论 收藏
分享
美团 后端开发 总包n(15%是股票)
点赞 评论 收藏
分享
评论
3
收藏
分享
牛客网
牛客企业服务