【題解】NOI2014動物園
阿新 • • 發佈:2018-02-07
字符 可能 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動物園