BZOJ 3483 SGU505 Prefixes and suffixes(询问在线版) Hash,预处理,神做法

Description

GAL发现了N个特殊的字母序列,由小写字母组成。小L认为,对于两个字符串s1,s2,若s1是某个特殊序列的前缀,s2是该特殊序列的后缀,则称s1,s2被这个序列拥有。

现在小L给出M对s1,s2,对于每对字符串,问它们被几个特殊序列拥有。

Input

第1行一个整数N。

接下来N行,每行一个字符串,代表N个特殊序列。

第N+2行一个整数M。

接下来M行每行一对s1,s2用空格隔开。S1,s2是经过加密的。

设上一问的答案为lastans。解密方法是将s1,s2所有字母向后移动lastans个单位,这时你要把小写字母表当作一个环,比如z的下一个字母是a。

Output

对于每次询问操作,输出一个非负整数表示答案。
Sample Input
3

aaaaa

abacabaa

avtobus

6

a a

y yy

yy y

zzzzz zzzz

zazb bzaz

abac a

Sample Output
2

2

1

1

0

1

HINT

设N个特殊序列总长为L1,所有M组询问总长为L2。L1,L2<=2000000.N<=2000,M<=100000

解法: 自己只能写个自然溢出的hash,但是是ac不了的。看到了一位神牛留下的神奇做法,对于询问俩串长都小于20的,可知这种类型有解的情况只有2000*20*20种,把这些情况预处理答案用map存一下,然后直接查表,其余的暴力,我没有判,直接全部存了表。。。。然后发现也ac了。。

//BZOJ 3483

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long uLL;
const uLL mod = 1e9+7;
const int maxn = 2010;
int n, m, l[maxn];
string a[maxn];
char ch[2000010];
vector <uLL> L[maxn], R[maxn];
int ans;

void input()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%s", ch);
        l[i] = strlen(ch);
        for(int j = 0; j < l[i]; j++) a[i] += ch[j];
    }
    uLL last;
    for(int i = 1; i <= n; i++){
        last = 171;
        for(int j = 0; j < l[i]; j++){
            last = last*mod+a[i][j];
            L[i].push_back(last);
        }
    }
    for(int i = 1; i <= n; i++){
        last = 171;
        for(int j = 0; j < l[i]; j++){
            last = last*mod+a[i][l[i]-j-1];
            R[i].push_back(last);
        }
    }
}

void work()
{
    scanf("%d", &m);
    ans = 0;
    while(m--)
    {
        uLL x = 171, y = 171;
        scanf("%s", ch);
        int len1 = strlen(ch);
        for(int j = 0; j < len1; j++) ch[j] = (ch[j] - 'a' + ans)%26+'a';
        for(int j = 0; j < len1; j++) x = x*mod + ch[j];
        scanf("%s", ch);
        int len2 = strlen(ch);
        for(int j = 0; j < len2; j++) ch[j] = (ch[j] - 'a' + ans)%26 + 'a';
        for(int j = 0; j < len2; j++) y = y*mod + ch[len2-j-1];
        ans = 0;
        for(int i = 1; i <= n; i++){
            if(L[i][len1-1]==x && R[i][len2-1] == y) ans++;
        }
        printf("%d\n", ans);
    }
}

void output()
{
    //printf("%d\n", ans);
}

int main()
{
    input();
    work();
    //output();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值