• 2021牛客寒假算法基础集训营4 B. 武辰延的字符串(二分/Hash/exkmp)


    链接:https://ac.nowcoder.com/acm/contest/9984/B
    来源:牛客网

    题目描述

    众所周知,武辰延很喜欢字符串。

    这天,他对着两个字符串 s 和 t 发呆,他发现这两个串的前缀有很多相似的地方,s 的两个前缀连接起来竟也是 t 的前缀。

    武辰延想知道有多少对 s 的非空前缀连接起来是 t 的前缀。

    形式化地讲,我们把 sisi 看作字符串 s 长度为 i 的前缀。

    对于一对前缀 (si,sj)(si,sj) (允许 i=j)而言,当满足 si+sj=ti+jsi+sj=ti+j 时,我们认为这两个 s 的前缀拼接后等于 t 的一个前缀。

    两对 s 的前缀 (si,sj)(si,sj) 与 (si′,sj′)(si′,sj′) 不同当且仅当 i≠i′i=i′ 或 j≠j′j=j′ 。

    输入描述:

    第一行一个字符串 s 。
    
    第二行一个字符串 t 。
    
    其中 1≤∣s∣,∣t∣≤1e51≤∣s∣,∣t∣≤1e5 ,只包含小写字母。
    

    输出描述:

    输出一行一个整数,表示满足条件的前缀的对数。
    

    示例1

    输入

    复制

    aab
    aaa
    

    输出

    复制

    3
    

    比赛的时候想到hash了但是没想到可以用单调性二分(s_j)的长度,也不会exkmp...我是废物

    首先易知对于((s_i, s_j))(s_i)一定等于(t_i)。因此枚举s的前缀是省略不了的。因此第一重循环枚举i,如果(s_i)(t_i)相等的话(hash判断),说明前半截拼上了,现在需要找后半截。后半截(s_j)也是s的前缀,同时它也是t截去前缀(t_i)后剩下部分的前缀即(t[i + 1,-1])。注意到如果有(s_j)满足条件,那么(s_{i - 1}, s_{j - 2}...s_1)等前缀也都满足条件,因此后半部分的右端点下标j具有单调性,可以直接二分这个下标j(或者说第二个前缀sj的长度),check的时候检查(s_j)(t[i + 1,i + j])的哈希值是否相等,找到满足条件的最长的前缀,其长度即对于最终答案的贡献,累加过去即可。

    二分的时候左右端点貌似有点问题,代码能过仅供参考。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    const int N=100010,P=131;
    char s[N], t[N];
    typedef unsigned long long ULL;
    ULL h1[N], p1[N], h2[N], p2[N];
    int lens, lent;
    ULL get1(int l,int r)
    {
        return h1[r] - h1[l-1] * p1[r-l+1];
    }
    ULL get2(int l,int r)
    {
        return h2[r] - h2[l-1] * p2[r-l+1];
    }
    long long ans = 0;
    int main()
    {
        freopen("data.txt", "r", stdin);
        scanf("%s", s + 1);
        scanf("%s", t + 1);
        lens = strlen(s + 1), lent = strlen(t + 1);
        p1[0] = 1;
        p2[0] = 1;
        for(int i = 1;i <= lens; i++)
        {
            p1[i] = p1[i-1] * P;
            h1[i] = h1[i-1] * P +s[i];
        }
        for(int i = 1;i <= lent; i++)
        {
            p2[i] = p2[i-1] * P;
            h2[i] = h2[i-1] * P +t[i];
        }
        for(int i = 1; i <= lens; i++)
        {
            if(get1(1, i) == get2(1, i))
            {
                int l = 1, r = lens + 1, mid;
                while(l < r)
                {
                    mid = (l + r) >> 1;
                    if(i + mid <= lent && get1(1, mid) == get2(i + 1, i + mid))
                    {
                        l = mid + 1;
                    }
                    else r = mid;
                }
                ans += l - 1;
            }
        }
        cout << ans;
        return 0;
    }
    
  • 相关阅读:
    Python 认识元组
    Python 认识字典
    Python 字符串中常见的一些方法续
    python 打印 A ~ Z
    Python3的print怎么让它不换行
    Python 中的 lstrip、rstrip、strip
    Python判断一个字符串是否包含指定字符串的方法
    Python 字符串中常见的一些方法
    Python 认识字符串
    苹果电脑MacbookPro双开微信!
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/14418803.html
Copyright © 2020-2023  润新知