【BZOJ】4084: [Sdoi2015]双旋转字符串 哈希

传送门:bzoj4084


题解

题面非常坑!!!
要求的是 Si+Tj S i + T j ,但实际上任意 Tj+Si T j + S i 也可以算。
AC的程序跑出的两组数据:
input:

1 1 2 6
ab
abcdcd

output:

1

(按题意应该是0)
input:

1 1 2 2
aa
aa 

output:

2 

(显然为1)

好吧,说说错误的标算:
实际上很简单很暴力, O(n2) O ( n 2 ) 预处理出长度小的集合的每个字符串的哈希值存在 map m a p 里(数据水到单哈希可以过),再枚举另一个集合里的字符串,由于其长度必然 len1+len22 ≥ l e n 1 + l e n 2 2 ,所以可以确定出完整字符串,在 map m a p 里加上答案即可。


代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#define RI register
using namespace std;
typedef long long ll;
const int mod=958244353,hs=47,N=4e6+10;
string s[N],t[N];ll ans; 
int n,m,la,lb,pw[N],f[N];
map<int,int>mp;

inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}
inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}

int main(){
    RI int i,j,res,mid,upp,tp,len;
    scanf("%d%d%d%d",&n,&m,&la,&lb);
    len=la+lb;mid=len>>1;
    pw[0]=1;for(i=1;i<=len;++i) pw[i]=mul(pw[i-1],hs);
    if(la<lb){
        for(i=1;i<=n;++i) cin>>t[i];
        for(i=1;i<=m;++i) cin>>s[i];
        swap(n,m);swap(la,lb);
    }else{
        for(i=1;i<=n;++i) cin>>s[i];
        for(i=1;i<=m;++i) cin>>t[i]; 
    }
       for(i=1;i<=m;++i){
            for(res=0,j=0;j<lb;++j) 
              res=ad(1ll*res*hs%mod,t[i][j]-'a'+1);
            mp[res]++;
        }
        upp=la-mid;
        for(i=1;i<=n;++i){
            for(j=1;j<=la;++j) f[j]=ad(1ll*f[j-1]*hs%mod,s[i][j-1]-'a'+1);
            tp=dc(f[la],mul(f[la-upp],pw[upp]));
            for(j=1;j+upp-1<=mid && j<=mid;++j){
               res=dc(f[j+upp-1],mul(f[j-1],pw[upp]));
               if(res==tp)
                ans+=mp[ad(mul(dc(f[mid],mul(f[j+upp-1],pw[mid-j-upp+1])),pw[j-1]),f[j-1])];
            }
            for(;j<=mid;++j){
                res=ad(mul(dc(f[mid],mul(f[j-1],pw[mid-j+1])),pw[upp-mid+j-1]),f[upp-mid+j-1]);
                if(res==tp)
                 ans+=mp[dc(f[j-1],mul(f[upp-mid+j-1],pw[mid-upp]))];
            }
        }
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值