4084: [Sdoi2015]双旋转字符串

Description

给定两个字符串集合 S 和 T 。其中 S 中的所有字符串长度都恰好为 N ,而 T 中所有字符串长度都恰好为 M 。
且 N+M 恰好为偶数。如果记 S 中字符串全体为 S1,S2,…,STotalS ,而 T 中字符串全体为 T1,T2,…,TT
otalT 。现在希望知道有多少对

题解

hash 给大的集合中的每个字符串 把一段按mid分成两段 ,然后看看要是匹配的话需要小串是什么。。
然后需要的小串用个map记录一下就好了。。
最后累加很简单

CODE:

#include
#include
#include
#include
#include
typedef long long LL;
using namespace std;
const int MOD=10037;//乘的东西
const int MOD1=1000000007; 
const int N=4000005;
int S,T,n,m,o;
char ss[N],s1[N];
LL shen[N];//前i为的hash值
LL KK,KKK;
map<int,int> q;
struct qq
{
    int x;
}s[N*2];int cnt=0;
bool cmp (qq a,qq b){return a.xx;}
void add ()
{
    cnt=0;
    LL p1=0;
    for (int u=1;u<=n-o;u++) p1=((p1*MOD)%MOD1+ss[u+o]-'a')%MOD1;
    int len=0;
    for (int u=1;u<=o;u++) s1[++len]=ss[u];
    for (int u=1;u<=o;u++) s1[++len]=ss[u];
    shen[0]=0;
    for (int u=1;u<=len;u++) shen[u]=((shen[u-1]*MOD)%MOD1+s1[u]-'a')%MOD1;
    for (int u=1;u<=o;u++)//枚举开头在哪里 
    {
        LL a=((shen[u+n-o-1]-shen[u-1]*KKK%MOD1)%MOD1+MOD1)%MOD1;
        if (a!=p1) continue;

        a=((shen[u+o-1]-shen[u+n-o-1]*KK%MOD1)%MOD1+MOD1)%MOD1;
        s[++cnt].x=(int)a;
    }
    sort(s+1,s+1+cnt,cmp);s[0].x=-1;
    for (int u=1;u<=cnt;u++)
        if (s[u].x!=s[u-1].x)
            q[s[u].x]++;
}
int main()
{
    scanf("%d%d%d%d",&S,&T,&n,&m);o=(n+m)>>1;//中间端点是什么 
    KK=1;
    for (int u=1;u<=2*o-n;u++) KK=KK*MOD%MOD1;
    KKK=1;
    for (int u=1;u<=n-o;u++) KKK=KKK*MOD%MOD1;
    for (int u=1;u<=S;u++)
    {
        scanf("%s",ss+1);
        add();
    }
    int ans=0;
    for (int u=1;u<=T;u++)
    {
        scanf("%s",ss+1);
        LL p=0;
        for (int i=1;i<=m;i++) p=((p*MOD)%MOD1+ss[i]-'a')%MOD1;
        ans=ans+q[p];
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(hash)