关于 KMP
KMP 算法
1. 剪花布条
/*
Time: 1.10
Worker: Blank_space
Source: #10043. 「一本通 2.2 例 1」剪花布条
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#include<iostream>
#define emm(x) memset(x, 0, sizeof x)
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int p[1010], ans;
char s[1010], c[1010];
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
while(1)
{
cin >> s + 1;
if(s[1] == '#' && !s[2]) return 0;
cin >> c + 1; emm(p); ans = 0;
int m = strlen(s + 1), n = strlen(c + 1);
for(int i = 1, j = 0; i <= n; i++)
{
while(c[i + 1] != c[j + 1] && j) j = p[j];
p[i + 1] = c[i + 1] == c[j + 1] ? ++j : j;
}
for(int i = 0, j = 0; i < m; i++)
{
while(s[i + 1] != c[j + 1] && j) j = p[j];
j += (s[i + 1] == c[j + 1]);
if(j == n) ans++, j = 0;
}
printf("%d\n", ans);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
2. Power Strings
/*
Time: 1.10
Worker: Blank_space
Source: #10044 「一本通 2.2 例 2」Power Strings
题目重了 这个题在字符串哈希那里做过...
用哈希对每一个串的判断同样可以优化到 O(1)
但感觉这个 kmp 的思路确实很神
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int p[C];
char s[C];
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
while(1)
{
scanf("%s", s + 1);
if(s[1] == '.') return 0;
int n = strlen(s + 1); p[1] = 0;
for(int i = 1, j = 0; i <= n; i++)
{
while(s[i + 1] != s[j + 1] && j) j = p[j];
p[i + 1] = s[i + 1] == s[j + 1] ? ++j : 0;
}
if(!(n % (n - p[n]))) printf("%d\n", n / (n - p[n]));
else puts("1");
}
// fclose(stdin);
// fclose(stdout);
return 0;
}
3. Radio Transmission
/*
Time: 1.10
Worker: Blank_space
Source: #10045. 「一本通 2.2 练习 1」Radio Transmission
结论题: ans = n - p[n]
p[n] 是 n 找到的上一个位置 可以理解为将 整个串向前移动 使得 n 位置与 p[n] 位置对应
不难看出移动前的串中从 1 至 p[n] 一定是由移动后的 1 的位置到移动前的 1 的位置构成的 例如:
1234 1234 1234 12
1234 1234 1234 12
所以 答案即为最大的公共前缀(后缀)
*/
/*--------------------------------------------*/
#include<cstdio>
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, p[C];
char s[C];
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = read(); scanf("%s", s + 1);
for(int i = 1, j = 0; i <= n; i++)
{
while(s[i + 1] != s[j + 1] && j) j = p[j];
p[i + 1] = s[i + 1] == s[j + 1] ? ++j : 0;
}
printf("%d", n - p[n]);
// fclose(stdin);
// fclose(stdout);
return 0;
}
4. OKR-Periods of Words
/*
Time: 1.13
Worker: Blank_space
Source: #10046. 「一本通 2.2 练习 2」OKR-Periods of Words
把题目翻译成人话:
求 给定字符串的所有子串除却其最小非空前缀 的长度和
*/
/*--------------------------------------------*/
#include<cstdio>
using namespace std;
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
long long n, p[C], ans;
char s[C];
/*------------------------------------变量定义*/
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
/*----------------------------------------函数*/
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = read(); scanf("%s", s + 1);
for(int i = 1, j = 0; i <= n; i++)
{
while(s[i + 1] != s[j + 1] && j) j = p[j];
p[i + 1] = s[i + 1] == s[j + 1] ? ++j : 0;
}
for(int i = 1, j = 1; i <= n; i++, j = i)
{
while(p[j]) j = p[j];
if(p[i]) p[i] = j;
ans += i - j;
}
printf("%lld", ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}
5. 似乎在梦中见过的样子
/*
Time: 1.9
Worker: Blank_space
Source: #10047. 「一本通 2.2 练习 3」似乎在梦中见过的样子
*/
/*---------------------------------------------------*/
#include<cstdio>
#include<cstring>
using namespace std;
/*---------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*---------------------------------------------------*/
long long k, p[B], ans, f[B];
char s[B];
/*---------------------------------------------------*/
int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*---------------------------------------------------*/
void kmp(char *s)
{
for(int i = 2, j = 0; s[i]; i++)
{
while(s[i] != s[j + 1] && j) j = p[j];
p[i] = s[i] == s[j + 1] ? ++j : 0;
}
for(int i = 1, j = 0; s[i]; i++)
{
while(s[i] != s[j + 1] && j) j = p[j];
j += s[i] == s[j + 1];
while(j << 1 >= i) j = p[j];
ans += j >= k;
}
}
/*---------------------------------------------------*/
int main()
{
// freopen("", "r", stdin);
// freopen("", "w", stdout);
scanf("%s", s + 1); k = read();
for(int i = 0; s[i + 1]; i++) kmp(s + i);
printf("%lld", ans);
// fclose(stdin);
// fclose(stdout);
}
gugugu~