1. 程式人生 > >【題解】NOI2014動物園

【題解】NOI2014動物園

字符 可能 namespace 標記 題目 pro gpo bits 後綴

傳送門:洛谷P2375

一直到寫到這道題目才發現我一直都理解了假的KMP……fail數組:fail[i]為從1-i(包含i)在內的字符串,相同的最長前後綴長度。

那麽我們可以先思考暴力:先求出所有的fail,再不斷往上跳,那麽跳到的節點中(fail<<1)<i的個數即為num值。但這樣的復雜度太高了,所以我們要進一步優化。

可以發現每一個節點所指向的fail節點是唯一的,但一個點可能是多個節點的fail,這是一個樹形的關系。且在這個樹形關系上,越靠近根節點的fail值也就越小。所以我們逐層標記Num值,直到找到符合條件的節點,則這個節點所標記的值就是它&它上方所有節點的個數。(若該點滿足條件,則在它之上的一定滿足)。

#include <bits/stdc++.h>
using namespace std;
#define maxn 10000
#define ll long long
#define p 1000000007
int fail[maxn], num[maxn];
char s[maxn];
ll solve()
{
    ll ans = 1;
    int len = strlen(s + 1);
    memset(fail, 0, sizeof(fail));
    memset(num, 0, sizeof(num));
    num[1] = 1;
    for(int i = 2
, j = 0; i <= len; i ++) { while(j && s[i] != s[j + 1]) j = fail[j]; if(s[i] == s[j + 1]) j ++; fail[i] = j; num[i] = num[j] + 1; } for(int i = 2, j = 0; i <= len; i ++) { while(j && s[i] != s[j + 1]) j = fail[j]; if
(s[i] == s[j + 1]) j ++; while((j << 1) > i) j = fail[j]; ans = (num[j] + 1) * ans; ans %= p; } return ans; } int main() { int T; scanf("%d", &T); while(T --) { scanf("%s", s + 1); printf("%lld\n", solve()); } return 0; }

【題解】NOI2014動物園