4084: [Sdoi2015]双旋转字符串

4084: [Sdoi2015]双旋转字符串

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 394   Solved: 161
[ Submit][ Status][ Discuss]

Description

给定两个字符串集合 S 和 T 。其中 S 中的所有字符串长度都恰好为 N ,而 T 中所有字符串长度都恰好为 M 。且 N+M 恰好为偶数。
如果记 S 中字符串全体为 S1,S2,...,STotalS ,而 T 中字符串全体为 T1,T2,...,TTotalT 。
现在希望知道有多少对 <i,j> ,满足将 Si 和 Tj 拼接后得到的字符串 Si+Tj 满足双旋转性。
一个长度为偶数字符串 W 可以表示成两段长度相同的字符串的拼接,即 W=U+V。如果 V 可以通过 U 旋转得到,则称 W 是满足双旋转性的。比如说字符串 U=“vijos”可以通过旋转得到“ijosv”,“josvi”,“osvij” 或“svijo”。那么“vijosjosvi”就是满足双旋转性的字符串。

Input

第一行输入四个正整数,分别为 TotalS,TotalT,N 和 M,依次表示集合 S 的大小,集合 T 的大小,集合 S 中字符串的长度和集合 T 中字符串的长度。
之后 TotalS 行,依次给出 S 中所有的字符串 Si,1≤i≤TotalS。保证每一个字符串长度都恰为 N ,且字符串只由 26 个小写字母组成。
之后 TotalT 行,依次给出 T 中所有的字符串 Ti,1≤i≤TotalT。保证每一个字符串长度都恰为 M ,且字符串只由 26 个小写字母组成。
1≤N≤100;1≤M≤100;1≤TotalS≤100;1≤Total^T≤100,2≤N*TotalS+M*TotalT≤4×10^6,N>=M

Output

输出一个整数,表示满足要求的数字对 <i,j> 有多少个。

Sample Input

4 4 7 3
vijosvi
josvivi
vijosos
ijosvsv
jos
vij
ijo
jos

Sample Output

6

HINT

Source

[ Submit][ Status][ Discuss]



一开始题意没读好,结果做不出来。。。。

大概是要统计有多少对<i,j>使得串Si + Tj左右两半循环同构

不妨假设N >= M,反之情况类似

对于T中的每个字符串,先hash一下存在一个桶里面

暴力枚举S中的每个字符串,假设当前枚举的是Si

对于这个串,记mid = (N + M) / 2,Si的mid + 1 ~ N位在合并以后显然是给右边的半部分用的

可以在1 ~ mid位中暴力查找一下那些位置往后N - mid位与这个短串相等

每次找到一个位置,对于1 ~ mid的剩下的字符,后半部在前前半部放后显然就确定了Tj

这时候只要在桶里面查询一下这样的Tj有多少就行了


记得统计的时候不能重复,就是一类Tj对于每个Si只能用一次

用了双hash + 离散化处理这个桶,,O(|S| * logM),复杂度有点感人。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
 
const int maxn = 4E6 + 4;
const int mod1 = 1000000007;
const int mod2 = 99999983;
typedef long long LL;
 
struct Hash{
    int h1,h2; Hash(){h1 = h2 = 0;}
    Hash(int h1,int h2): h1(h1),h2(h2){}
    Hash operator * (const Hash &B)
    {
        Hash ret;
        ret.h1 = 1LL * h1 * B.h1 % mod1;
        ret.h2 = 1LL * h2 * B.h2 % mod2;
        return ret;
    }
    Hash operator *= (const Hash &B)
    {
        h1 = 1LL * h1 * B.h1 % mod1;
        h2 = 1LL * h2 * B.h2 % mod2;
    }
    Hash operator += (const int &B)
    {
        h1 += B; if (h1 >= mod1) h1 -= mod1;
        h2 += B; if (h2 >= mod2) h2 -= mod2;
    }
    Hash operator += (const Hash &B)
    {
        h1 += B.h1; if (h1 >= mod1) h1 -= mod1;
        h2 += B.h2; if (h2 >= mod2) h2 -= mod2;
    }
    Hash operator -= (const Hash &B)
    {
        h1 -= B.h1; if (h1 < 0) h1 += mod1;
        h2 -= B.h2; if (h2 < 0) h2 += mod2;
    }
    bool operator < (const Hash &B) const
    {
        if (h1 < B.h1) return 1;
        if (h1 > B.h1) return 0;
        return h2 < B.h2;
    }
    bool operator == (const Hash &B) const {return h1 == B.h1 && h2 == B.h2;}
    bool operator != (const Hash &B) const {return h1 != B.h1 || h2 != B.h2;}
}p;
 
int n,m,ta,tb,mid,cur,Cnt;
char ch[maxn];
 
vector <string> A,B;
vector <Hash> mi,v,h;
vector <int> cnt,vis;
 
Hash GetHash(int L,int R)
{
    Hash ret; if (L > R) return ret;
    ret = h[R]; if (L > 0) ret -= (h[L - 1] * mi[R - L + 1]);
    return ret;
}
 
void Solve1()
{
    int siz = n - mid; LL Ans = 0; mi.push_back(Hash(1,1));
    for (int i = 1; i <= n; i++) mi.push_back(mi[i - 1] * p);
    for (int i = 0; i < tb; i++)
    {
        string &s = B[i]; Hash now;
        for (int j = 0; j < m; j++) now *= p,now += s[j];
        v.push_back(now);
    }
    sort(v.begin(),v.end()); cnt.push_back(1);
    for (int i = 1; i < v.size(); i++)
        if (v[i] == v[i - 1]) ++cnt[cur];
        else v[++cur] = v[i],cnt.push_back(1);
    while (v.size() - 1 > cur) v.pop_back();
    for (int i = 0; i <= cur; i++) vis.push_back(0);
    for (int i = 0; i < n; i++) h.push_back(Hash(0,0));
    for (int i = 0; i < ta; i++)
    {
        string &s = A[i]; Hash now; ++Cnt;
        for (int j = 0; j < mid; j++) now *= p,now += s[j],h[j] = now;
        now = Hash(0,0);
        for (int j = mid; j < n; j++) now *= p,now += s[j];
        for (int j = 0; j <= mid - siz; j++)
        {
            if (GetHash(j,j + siz - 1) != now) continue;
            Hash Now = GetHash(j + siz,mid - 1);
            Now *= mi[j]; Now += GetHash(0,j - 1);
            int pos = lower_bound(v.begin(),v.end(),Now) - v.begin();
            if (pos < v.size() && v[pos] == Now && vis[pos] != Cnt) Ans += 1LL * cnt[pos],vis[pos] = Cnt;
        }
    }
    cout << Ans << endl;
}
 
void Solve2()
{
    int siz = m - mid; LL Ans = 0; mi.push_back(Hash(1,1));
    for (int i = 1; i <= m; i++) mi.push_back(mi[i - 1] * p);
    for (int i = 0; i < ta; i++)
    {
        string &s = A[i]; Hash now;
        for (int j = 0; j < n; j++) now *= p,now += s[j];
        v.push_back(now);
    }
    sort(v.begin(),v.end()); cnt.push_back(1);
    for (int i = 1; i < v.size(); i++)
        if (v[i] == v[i - 1]) ++cnt[cur];
        else v[++cur] = v[i],cnt.push_back(1);
    while (v.size() - 1 > cur) v.pop_back();
    for (int i = 0; i <= cur; i++) vis.push_back(0);
    for (int i = 0; i < m; i++) h.push_back(Hash(0,0));
    for (int i = 0; i < tb; i++)
    {
        string &s = B[i]; Hash now; ++Cnt;
        for (int j = siz; j < m; j++) now *= p,now += s[j],h[j] = now;
        now = Hash(0,0);
        for (int j = 0; j < siz; j++) now *= p,now += s[j];
        for (int j = siz; j <= m - siz; j++)
        {
            if (GetHash(j,j + siz - 1) != now) continue;
            Hash Now = GetHash(j + siz,m - 1);
            Now *= mi[j - siz]; Now += GetHash(siz,j - 1);
            int pos = lower_bound(v.begin(),v.end(),Now) - v.begin();
            if (pos < v.size() && v[pos] == Now && vis[pos] != Cnt) Ans += 1LL * cnt[pos],vis[pos] = Cnt;
        }
    }
    cout << Ans << endl;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    p.h1 = 233; p.h2 = 131;
    cin >> ta >> tb >> n >> m; mid = n + m >> 1;
    for (int i = 0; i < ta; i++)
    {
        scanf("%s",ch); string s;
        for (int j = 0; j < n; j++) s += ch[j];
        A.push_back(s);
    }
    for (int i = 0; i < tb; i++)
    {
        scanf("%s",ch); string s;
        for (int j = 0; j < m; j++) s += ch[j];
        B.push_back(s);
    }
    if (n >= m) Solve1(); else Solve2();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值