zoukankan      html  css  js  c++  java
  • 二分+哈希

     链接: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。

  • 相关阅读:
    RegularExpressions(正则表达式)
    IntelliJ Idea工具使用
    JavaScript总结
    Ajax技术基础
    配置Nginx作为反向代理服务器
    SpringBoot下的Dubbo和Zookeeper整合
    springSecurity初识练气初期
    Spring Security拦截器加载流程分析练气中期
    Spring Security认证流程分析练气后期
    springBoot整合spring security+JWT实现单点登录与权限管理前后端分离筑基中期
  • 原文地址:https://www.cnblogs.com/lipu123/p/14423924.html
Copyright © 2011-2022 走看看