二分+哈希

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

原文地址:https://www.cnblogs.com/lipu123/p/14423924.html