BZOJ3670 - 动物园(kmp)

题目

求出一个num数组一一对于字符串S的前i个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作num[i]。
输出\(\prod_{i=1}^L(num[i]+1)\)对1,000,000,007取模的结果即可。

题解

先求出next数组。然后预处理一个tmp数组,它是num数组的后缀与前缀可以重叠的版本。
和next数组求法类似,假设位置i前的tmp数组都求出来了,那么

  • tmp[0]=-1
  • 当j-1或s[i]s[j]时,tmp[i]=tmp[j]+1
  • 否则j=next[j],继续判断

而求num数据就是加个2*(j + 1)<=(i + 1)的限制条件,其它步骤和求tmp数组一模一样。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define pb push_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 1e6 + 10;
const int M = 1e9 + 7;
const double eps = 1e-5;
char s[N];
int nt[N];
int num[N];
int tmp[N];
void getnext(char t[]) {
    int i = 0, j = -1;
    nt[i] = num[i] = tmp[i] = j;
    while(t[i]) {
        if(j == -1 || t[i] == t[j]) {
            i++;
            j++;
            nt[i] = j;
            tmp[i] = tmp[j] + 1;
            
        } else {
            j = nt[j];
        }
    }
    i = 0, j = -1;
    while(t[i]) {
        if(j == -1 || (t[i] == t[j] && 2 * (j + 1) <= (i + 1))) {
            i++;
            j++;
            num[i] = tmp[j] + 1;
        } else {
            j = nt[j];
        }
    }
}


int main() {
    IOS;
    int t;
    cin >>t;
    while(t--) {
        memset(num, 0, sizeof num);
        cin >> s;
        int len = strlen(s);
        getnext(s);
        int ans = 1;
        for(int i = 1; i <= len; i++) {
            ans = 1ll * ans * (num[i] + 1) % M;
            //cout << num[i] << " ";
        }
        //cout << endl;
        cout << ans << endl;
    }
}
posted @ 2020-08-14 15:15  limil  阅读(102)  评论(0编辑  收藏  举报