HDU - 1711 Number Sequence
思路
KMP模板
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000010;
int s[N], p[N];
int ne[N];
int main() {
int n, m;
int T;
scanf("%d", &T);
while(T --) {
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i ++) {
scanf("%d", s + i);
}
for(int i = 1; i <= m; i ++) {
scanf("%d", p + i);
}
int ans = -1;
for (int i = 2, j = 0; i <= m; i ++) {
while(j && p[j + 1] != p[i])
j = ne[j];
if(p[j + 1] == p[i])
j++;
ne[i] = j;
}
for(int i = 1, j = 0; i <= n; i ++) {
while(j && p[j + 1] != s[i])
j = ne[j];
if(p[j + 1] == s[i])
j++;
if(j == m) {
ans = i - m + 1;
break;
}
}
printf("%d\n", ans);
}
return 0;
}
HDU - 1686 Oulipo
思路
求模式串在匹配串出现的次数
还是KMP模板
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000010;
char s[N], p[N];
int ne[N];
int main() {
int T;
scanf("%d", &T);
while(T --) {
scanf("%s", p + 1);
scanf("%s", s + 1);
int n = strlen(s + 1);
int m = strlen(p + 1);
int ans = 0;
for (int i = 2, j = 0; i <= m; i ++) {
while(j && p[j + 1] != p[i])
j = ne[j];
if(p[j + 1] == p[i])
j++;
ne[i] = j;
}
for(int i = 1, j = 0; i <= n; i ++) {
while(j && p[j + 1] != s[i])
j = ne[j];
if(p[j + 1] == s[i])
j++;
if(j == m) {
ans++;
j = ne[j];
}
}
printf("%d\n", ans);
}
return 0;
}
HDU - 2087剪花布条
题意
问能从匹配串裁剪多少个模式串
思路
还还还是模板题
只需要当找到一个匹配的,将j = 0
而不是j=ne[i]
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
char s[N], p[N];
int ne[N];
int main() {
while(~scanf("%s", s + 1)) {
if(s[1] == '#')
break;
scanf("%s", p + 1);
int n = strlen(s + 1);
int m = strlen(p + 1);
int ans = 0;
for (int i = 2, j = 0; i <= m; i ++) {
while(j && p[j + 1] != p[i])
j = ne[j];
if(p[j + 1] == p[i])
j++;
ne[i] = j;
}
for(int i = 1, j = 0; i <= n; i ++) {
while(j && p[j + 1] != s[i])
j = ne[j];
if(p[j + 1] == s[i])
j++;
if(j == m) {
ans++;
j = 0;
}
}
printf("%d\n", ans);
}
return 0;
}
HDU - 3746 Cyclic Nacklace
题意
求加多少字符能将其字符串变为 至少循环两次 的字符串
例如:
1.abcab
需要加一个c,-> abcabc
2.abcd
需要加abcd,-> abcdabcd
思路
最小循环节问题,求ne数组即可
令字符串的长度为n
最小循环节len = n - ne[n]
则分情况判断即可
若要添加的情况,则add = len - (ne[n] % len)
若不用添加的情况, add = 0
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100010;
char p[N];
int ne[N];
int main() {
int T;
scanf("%d", &T);
while(T --) {
scanf("%s", p + 1);
int n = strlen(p + 1);
// 求ne数组
for (int i = 2, j = 0; i <= n; i ++) {
while(j && p[j + 1] != p[i])
j = ne[j];
if(p[j + 1] == p[i])
j++;
ne[i] = j;
}
// 最小循环节长度
int len = n - ne[n];
// 需要添加的字符个数
int ans;
if(len != n && n % len == 0) {
ans = 0;
}
else {
ans = len - (ne[n] % len);
}
printf("%d\n", ans);
}
return 0;
}
HDU - 1358 Period
思路
和上题类似
记住公式(模拟一下就知道)
最小前缀循环节: len = i - ne[i]
具有完整的循环节判断:i % (len) == 0
并注意求循环次数时必须满足:i / (i - ne[i]) > 1
,即循环次数不能为1
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000010;
char p[N];
int ne[N];
int main() {
int id = 0;
int n;
while(~scanf("%d", &n) && n){
printf("Test case #%d\n", ++ id);
scanf("%s", p + 1);
for (int i = 2, j = 0; i <= n; i ++) {
while(j && p[j + 1] != p[i])
j = ne[j];
if(p[j + 1] == p[i])
j++;
ne[i] = j;
}
for (int i = 2; i <= n; i ++) {
if(i % (i - ne[i]) == 0 && i / (i - ne[i]) > 1) {
printf("%d %d\n", i, i / (i - ne[i]));
}
}
puts("");
}
return 0;
}
POJ - 2406 Power Strings
思路
还是最小循环节问题
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 1000010;
char p[N];
int ne[N];
int main() {
while(~scanf("%s", p + 1) && p[1] != '.') {
int n = strlen(p + 1);
for (int i = 2, j = 0; i <= n; i ++) {
while(j && p[j + 1] != p[i])
j = ne[j];
if(p[j + 1] == p[i])
j++;
ne[i] = j;
}
int len = (n - ne[n]);
if(n % len == 0)
printf("%d\n", n / len);
else
printf("1\n");
}
return 0;
}
POJ - 2752 Seek the Name, Seek the Fame
思路
运用ne[i]数组的性质
它记录的就是当前1~i
序列 的最大相同前后缀的长度
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 400010;
char p[N];
int ne[N];
int stk[N];
int top;
int main() {
while(~scanf("%s", p + 1)) {
top = -1;
int n = strlen(p + 1);
for (int i = 2, j = 0; i <= n; i ++) {
while(j && p[j + 1] != p[i])
j = ne[j];
if(p[j + 1] == p[i])
j++;
ne[i] = j;
}
stk[++top] = n;
for (int i = ne[n]; i; i = ne[i]) {
stk[++top] = i;
}
while(top != -1) {
printf("%d ", stk[top--]);
}
puts("");
}
return 0;
}
题意
找公共连续子串
思路
暴力即可,数据过小,不用kmp加速都行
暴力枚举第一个串的所有子串,check其他串是否能匹配成功,若可以,则更新答案
代码
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 65;
char s[11][N], p[N], ans[N];
int n, m, mx, t, k, ne[N];
void getNext() {
ne[1] = 0;
for (int i = 2, j = 0; i <= n; i++) {
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j++;
ne[i] = j;
}
}
bool kmp(int m) {
// kmp匹配加速
getNext();
for (int i = 1, j = 0; i <= 60; i++) {
while (j && s[m][i] != p[j + 1]) j = ne[j];
if (s[m][i] == p[j + 1]) j++;
if (j == n) return true;//匹配成功
}
return false;
}
// 对所有字符串进行匹配, 是否都存在模式串
bool ok() {
for (int i = 1; i <= m; i++) {
if (!kmp(i)) return false;
}
return true;
}
int main() {
scanf("%d", &t);
while (t--) {
mx = 0;
scanf("%d", &m);
for (int i = 1; i <= m; i++) scanf("%s", s[i] + 1);
//枚举所有子串
for (int i = 1; i <= 60; i++) {
for (int j = i; j <= 60; j++) {
for (int k = i; k <= j; k++) p[k - i + 1] = s[1][k];
n = j - i + 1;
// 因为之前p数组是复用的, 这里加个'\0',代表字符串到这结束, 防止后面的字符串操作出错
p[n + 1] = '\0';
if (ok()) {
if (n > mx) { mx = n; memcpy(ans + 1, p + 1, sizeof(p));}
// 保证字典序小
else if(n == mx && strcmp(ans + 1, p + 1) > 0) memcpy(ans + 1, p + 1, sizeof(p));
}
}
}
if (mx < 3) printf("no significant commonalities\n");
else printf("%s\n", ans + 1);
}
return 0;
}
POJ - 2185 Milking Grid
题意
求最小循环矩阵
思路
还是最小循环节问题。
只不过一维变为二维
- 求每行的最小循环节,搞个LCM得ans1
- 求每列的最小循环节,搞个LCM得ans2
- 答案 = ans1 * ans2
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 10010;
int n, m;
int ne[N];
char str[N][N];
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
int getr(int pos) {
memset(ne, 0, sizeof ne);
for (int i = 2, j = 0; i <= m; i ++) {
while(j && str[pos][i] != str[pos][j + 1])
j = ne[j];
if(str[pos][i] == str[pos][j + 1])
j++;
ne[i] = j;
}
return m - ne[m];
}
int getl(int pos) {
memset(ne, 0, sizeof ne);
for (int i = 2, j = 0; i <= n; i ++) {
while(j && str[i][pos] != str[j + 1][pos])
j = ne[j];
if(str[i][pos] == str[j + 1][pos])
j++;
ne[i] = j;
}
return n - ne[n];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++)
scanf("%s", str[i] + 1);
int ans1 = 1;
for (int i = 1; i <= n; i ++) {
ans1 = lcm(ans1, getr(i));
}
if(ans1 > m)
ans1 = m;
int ans2 = 1;
for (int i = 1; i <= m; i ++)
ans2 = lcm(ans2, getl(i));
if(ans2 > n)
ans2 = n;
printf("%d\n", ans1 * ans2);
return 0;
}