Codeforces Gym101205D:Fibonacci Words(KMP+递推)

Gym 101205D

题意:f[0] = "0", f[1] = "1",接下来f[i] = f[i-1] + f[i-2],相当于字符串拼接。然后给出一个n和一个串s,问f[n]里面有多少个s。

思路:在int范围内的f[n]是n=31的时候,但是匹配的s的长度只有1e5,这时候n=27刚好大于它,因此可以先求解出n<=27的串的情况,然后由这些串来得到n>27的时候的情况。

比赛的时候想着递归算,结果爆栈。

看别人的代码之后我的理解是这样的:

(1)当n<=28的时候,可以直接kmp算。

(2)当n>28的时候,我们可以得到下面的递推式:(ans[i]表示f[i]里面有多少个s.可以先kmp求出ans[i](25 <= i <= 28) ).

先定义odd = ans[27] - ans[26] - ans[25](即得到s[27]由s[26]和s[25]拼接起来的时候能多得到的答案).

even = ans[28] - ans[27] - ans[26](即得到s[28]由s[27]和s[26]拼接起来的时候能多得到的答案).

因为len(s[27])和len(s[28])一定大于匹配的s,因此不用每次都暴力求解拼接起来的时候能多得到的答案,只需要在i为奇数的时候加上odd,i为偶数的时候加上even就可以得到拼接起来能多得到的答案了。

一开始以为奇偶是一样的情况,但是WA8之后问别人才知道是不一样的。

找6号串和找5号串的情况,可以发现6号串可以出现在8号串连接处而不能出现在7和9号串连接处。5号串能出现在7和9号的连接处但是不能出现在8号串的连接处,所以应当是分开考虑的。

这就是WF最水的题目之一...

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define N 105
 4 #define M 100010
 5 typedef long long LL;
 6 int nxt[M], len[N], sz;
 7 string str, s[N];
 8 LL ans[N];
 9 
10 void Make_Next() {
11     int i, j; j = nxt[0] = -1; i = 0;
12     while(i < sz) {
13         while(j != -1 && str[i] != str[j]) j = nxt[j];
14         if(str[++i] == str[++j]) nxt[i] = nxt[j];
15         else nxt[i] = j;
16     }
17 }
18 
19 LL Kmp(int id) {
20     int i = 0, j = 0, ans = 0;
21     while(i < len[id]) {
22         while(-1 != j && s[id][i] != str[j]) j = nxt[j];
23         i++; j++;
24         if(j >= sz) { ans++; j = nxt[j]; }
25     }
26     return ans;
27 }
28 
29 int main() {
30     int n, cas = 1;
31     s[0] = "0", s[1] = "1"; len[0] = len[1] = 1;
32     for(int i = 2; i <= 28; i++) s[i] = s[i-1] + s[i-2], len[i] = len[i-1] + len[i-2];
33     while(~scanf("%d", &n)) {
34         cin >> str;
35         sz = str.length(); Make_Next();
36         if(n <= 28) { printf("Case %d: %lld
", cas++, Kmp(n)); continue; }
37         for(int i = 25; i <= 28; i++) ans[i] = Kmp(i);
38         LL odd = ans[27] - ans[26] - ans[25], eve = ans[28] - ans[27] - ans[26]; // 拼接起来能够得到的贡献
39         for(int i = 29; i <= n; i++) {
40             ans[i] = ans[i-1] + ans[i-2];
41             if(i & 1) ans[i] += odd;
42             else ans[i] += eve;
43         }
44         printf("Case %d: %lld
", cas++, ans[n]);
45     }
46     return 0;
47 }
原文地址:https://www.cnblogs.com/fightfordream/p/6610978.html