<span>Luogu P3181 【[HAOI2016]找相同字符】</span>

Description

传送门


Solution

这题就是让求两个串的相同子串的个数。

众所周知,字符串所有的子串就是字符串所有的后缀的所有前缀。

利用这个性质我们可以将问题转化,变成求两个字符串的所有后缀的\(lcp\)的长度的和。

求后缀的\(lcp\)我们可以使用\(SA\)

将两个字符串连接起来,中间位置放一个\(1\)隔开,两个字符串分别染上不同的颜色。

现在答案就变成了求每一堆中颜色不同的后缀的\(lcp\)长度,考虑\(height\)数组的性质,即\(lcp(i, j) = min_{k = i + 1}^j height_k\),我们发现在每个块内\(lcp\)的长度具有单调性。

考虑每一堆中不同颜色之间的\(lcp\)长度就相当于先对于一种颜色算在它之前出现的另外一种颜色和它的\(lcp\),再按照另外一种颜色算一遍。

我们用单调栈维护这个\(lcp\)长度,用\(sum\)记录单调栈内元素对答案的总贡献,\(stack\)中保存两个信息——\(height\)值的大小和有多少个贡献和它相同的元素。

每次我们将当前的后缀\(i\)加入单调栈,如果\(sa_{i - 1}\)的颜色不是我们要算贡献的颜色,那么它就可以对当前颜色造成贡献,\(sum += height_i\)

这时我们将现在的\(height_i\)入栈,考虑如果\(height_i <= height_{top}\),那么栈顶对答案的贡献就会变为\(height_i\),所以可以将\(height_i\)\(height_{top}\)合并,对答案的贡献是\(height_i\),贡献个数是\(num_i + num_{top}\)

合并完之后如果\(sa_i\)的颜色是我们要算贡献的颜色就加上所有栈内的贡献。


Code

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

#define int long long
typedef long long ll;

const int N = 400050;

int k, m, n, s[N], x[N], y[N], cnt[N], sa[N], rnk[N], height[N], col[N], l, stack[N][2], top;
char a[N], b[N];
ll ans, sum;

void Rsort()
{
    for (int i = 0; i <= m; i++) cnt[i] = 0;
    for (int i = 1; i <= n; i++) cnt[x[i]]++;
    for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
    for (int i = n; i >= 1; i--) sa[cnt[x[y[i]]]--] = y[i];
    return;
}

void SA()
{
    m = 128;
    for (int i = 1; i <= n; i++) x[i] = s[i], y[i] = i;
    Rsort();
    m = 0;
    for (int k = 1; m < n; k <<= 1)
    {
        m = 0;
        for (int i = n - k + 1; i <= n; i++) y[++m] = i;
        for (int i = 1; i <= n; i++) if (sa[i] > k) y[++m] = sa[i] - k;
        Rsort();
        for (int i = 1; i <= n; i++) y[i] = x[i];
        m = x[sa[1]] = 1;
        for (int i = 2; i <= n; i++)
        {
            if(y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k])
                x[sa[i]] = m;
            else x[sa[i]] = ++m;
        }  
    }
    for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
    for (int i = 1; i <= n; i++)
    {
        height[rnk[i]] = height[rnk[i - 1]] - 1;
        if (height[rnk[i]] < 0) height[rnk[i]] = 0;
        while (s[i + height[rnk[i]]] == s[sa[rnk[i] - 1] + height[rnk[i]]]) height[rnk[i]]++;
    }
}

signed main()
{
    n = 0; ans = 0;
    scanf("%s", a + 1);
    l = strlen(a + 1);
    for (int i = 1; i <= l; i++) s[++n] = a[i] + 1, col[i] = 1;
    s[++n] = 1;
    scanf("%s", b + 1);
    l = strlen(b + 1);    
    for (int i = 1; i <= l; i++) s[++n] = b[i] + 1, col[n] = 2;
    SA();
    for (int color = 1; color <= 2; color++)
    {
        top = sum = 0;
        for (int i = 1; i <= n; i++)
            if (height[i] == 0) sum = top = 0;
            else
            {
                int cnt = 0;
                if (col[sa[i - 1]] != color) 
                {
                    cnt++;
                    sum += height[i];
                }
                while(top && height[i] <= stack[top][1])
                {
                    cnt += stack[top][2];
                    sum -= 1LL * stack[top][2] * (stack[top][1] - height[i]);
                    top--;
                }
                stack[++top][1] = height[i]; stack[top][2] = cnt;
                if (col[sa[i]] == color) ans += sum;
            }
    }
    printf("%lld\n", ans);
    return 0;
}
全部评论

相关推荐

昨天 10:50
已编辑
门头沟学院 Java
鼠鼠华子无线实习,bg双九,通软岗位,论文,专利,竞赛都水过一点,秋招《非all&nbsp;in》选手,《泡池子泡到肿》选手,分享一下自己的时间线,给大家多一个参考。---实习末期,接口人电话沟通,最终决定求稳继续投递实习原部门---免机试,九月走完线下流程,开始入池---十月起开始保温,打听手中已拿offer,比较薪资,给出华子的预估职级和薪资(完全不给A的空间)---十月第二次保温,询问签约情况,各种暗示劝说留空白三方---十月底签约另一家公司,遂被降低优先级---十一月若干次常规保温信息(还有机会/稍晚一点/等这周。。。)---十二月告知部门有13的指标,愿意接受可以立刻发offer(难绷,妄图性...
蓦然回首一枝花:能体会楼主的心情,我投了华为无线的成研所,双9bg,被华子最后开了个13级的侮辱价 12.3打oc电话的时候接口人表示乐观等待就行,然后中间4周就开始不回消息或者拖四五天才回,翻来覆去就是“等审批结果”。 12月27号,我看应该是泡不出来了所以联系了部门流转,这时候接口人开始主动给我打电话告诉我马上就能出结果了,于是我也没继续流转。 12.31给我打电话说得降薪审批,薪资大概就是对应着13级的样子,但我当时因为投的是成都的,没有意识到薪资是按照上海开的,还以为这个薪资在成都是14级,加上那个时候我也“孝”劲上来了,想着能收我就行,于是答应了。 1.13开了出来,联系我了薪资,确认了下发现是13级,当时实在是接受不了,于是最终还是拒了。 拒的时候接口人告诉我说这个hc真的是他们争取了很久才争取到的,不过我一想到我12.3就打了oc电话,中间4周一直不搭理我或吊着我,最后12.31才告诉我争取不下来14级要降薪,也许争取真的要争取那么久吧,呵。 这个过程中也为华为拒了不少offer,大厂的、央企的、银行的都拒过,网上总说“华为没有发小奖状之前hr的话一个字都不要信”,当时没有放在心上,以为不会摊到我头上,现在来看当时也挺年轻气盛的。我感觉要不是中途我一直在烦hr,可能我就和楼主一样被泡死了吧,不过最后给开了个13级也和泡死没差,不过是被多侮辱了一次。 最后借楼主这个贴就只想跟后面的人提一个建议吧,还是那句说烂了的,“华为没有发小奖状之前hr的话一个字都不要信”,真的不要以为这样的情况不会出现在自己身上,不要拿自己的一辈子前途去送华为hr业绩。
点赞 评论 收藏
分享
人生一梦:24年我投暑期实习,它以我不是女的为理由拒绝了我查看图片
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务