• 二分+哈希


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

    输入描述:

    第一行一个字符串 s 。

    第二行一个字符串 t 。

    其中 1≤∣s∣,∣t∣≤1e5 ,只包含小写字母。

    输出描述:

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

    输入

    复制
    aab
    aaa

    输出

    复制
    3

    说明

    s1+s1=t2
    s1+s2=t3
    s2+s1=t3


    题意是找出字符串t tt的前缀,使之由字符串s ss的两个前缀组成,即t[i+j]==s[i]+s[j]t i + j = s i + s j t_{i+j}=s_i+s_jt,求满足条件的总对数。

    首先根据前缀的特性不难想到,必须要有t i = s i t_i=s_iti=si,将上面的式子变一下形就可以看出来了,s[i+j]-s[i]==s[j]
    就是s[i+j]-s[i]之后从i+1之后的字符串和j的前缀能匹配的最大长度就是就是i的贡献

    具体算法就是哈希和二分:

    哈希能够O(1)判断两个子串是否匹配(字符串哈希算法

    二分是因为我们在找到某个前缀匹配时,前缀的前缀也一定匹配,

    所以可以二分找到最长前缀,因为比它长的前缀都不匹配,比它短的所有前缀都匹配,这就满足二分使用前提的单调性。

    举个例子:

    aab
    aaab

    枚举i=1,有相同前缀为'a',为'aab',最长能匹配上的前缀为'aab',其长度为3,答案+3。

    枚举i=2,有相同前缀为'aa',为'ab',最长能匹配上的前缀为'a',其长度为1,答案+1。

     

    枚举i=3,没有相同前缀('aab' != 'aaa'),结束,答案为4。

    就是这样

    #include<iostream>
    #include<algorithm>
    #include<queue> 
    #include<cstring> 
    using namespace std;
    typedef unsigned long long ll; 
    const int maxn=3e5+100,base=131;
    ll sum1[maxn],sum2[maxn],p[maxn];
    char s1[maxn],s2[maxn];
    int n1,n2;
    ll has1(int l,int r){//0(1)获得s[l,r]的哈希值
        return sum1[r]-sum1[l-1]*p[r-l+1];
    }
    ll has2(int l,int r){
        if(r>n2) return 0;
        return sum2[r]-sum2[l-1]*p[r-l+1];
    }
    int main(){
        scanf("%s%s",s1+1,s2+1);
        n1=strlen(s1+1);
        n2=strlen(s2+1);
        sum1[0]=sum2[0]=0;
        p[0]=1;
        for(int i=1;i<=max(n1,n2);i++){
            if(i<=n1) sum1[i]=sum1[i-1]*base+s1[i];
            if(i<=n2) sum2[i]=sum2[i-1]*base+s2[i];
            p[i]=p[i-1]*base; 
        }
        ll ans=0;
        for(int i=1;i<=n2;i++){
            if(s1[i]==s2[i]){
                int l=1,r=n1;
                int temp=0;
                while(r>=l){
                    int mid=(l+r)/2;
                    if(has1(1,mid)==has2(i+1,i+mid)){
                        l=mid+1;
                        temp=mid;
                    }
                    else{
                        r=mid-1;
                    }
                }
                ans+=temp; 
            }
            else{
                break;
            }
        }
        cout<<ans<<endl;
    } 

    题意是找出字符串t tt的前缀,使之由字符串s ss的两个前缀组成,即t i + j = s i + s j t_{i+j}=s_i+s_jti+j=si+sj,求满足条件的总对数。

    首先根据前缀的特性不难想到,必须要有t i = s i t_i=s_iti=si,这是必要条件。那么枚举s sst tt的所有相同前缀s i s_isi,然后求t − s i t-s_itsi,找其能匹配上s ss的前缀s j s_jsj的最大长度,计入答案即可。

    具体算法就是哈希和二分:

    • 哈希能够O(1)判断两个子串是否匹配(字符串哈希算法

    • 二分是因为我们在找到某个前缀匹配时,前缀的前缀也一定匹配,所以可以二分找到最长前缀,因为比它长的前缀都不匹配,比它短的所有前缀都匹配,这就满足二分使用前提的单调性。

    举个例子:

    s串:aab
    t串:aaab

    枚举i=1,有相同前缀s i s_isi为’a’,t − s i t-s_itsi为’aab’,最长能匹配上s ss的前缀为’aab’,其长度为3,答案+3。

    枚举i=2,有相同前缀s i s_isi为’aa’,t − s i t-s_itsi为’ab’,最长能匹配上s ss的前缀为’a’,其长度为1,答案+1。

    枚举i=3,没有相同前缀(‘aab’ != ‘aaa’),结束,答案为4。

  • 相关阅读:
    焦虑来回走
    去省政府客串
    中国地质大学(北京)招生信息有点坑
    张桂梅校长再获殊荣,实至名归!她的故事值得一看再看……
    行内容转为列内容
    公文写作心得
    钟南山院士亲口说的“如何保持健康长寿
    VMware虚拟机出现“内部错误”如何解决?
    CI框架深入篇(2)一些基础的我之不知道的标准格式
    SQL语句学习记录(三)
  • 原文地址:https://www.cnblogs.com/lipu123/p/14423924.html
Copyright © 2020-2023  润新知