目录
- 前言
- 题目(kmp算法我觉得应该称为前缀函数算法)
- 1. Number Sequence HDU - 1711 (KMP模板题:在另一个字符串中找一个字符串出现的位置)
- 2. Oulipo HDU - 1686 (KMP模板题:求一个子串在另一个子串中出现的次数)
- 3. 剪花布条 HDU - 2087 (KMP模板题:求另一个字符串中最多可以在剪出多少个该字符串)
- 4. Cyclic Nacklace HDU - 3746 (前缀函数模板题:求字符串周期n-pi[n-1])
- 5. Period HDU - 1358 (前缀函数模板题:求字符串前缀的周期i+1-pi[i])
- 6. The Minimum Length HUST - 1010 (求周期水题:略了)
- 7. Power Strings POJ - 2406 (求周期水题+1,略了略了)
- 8. Seek the Name, Seek the Fame POJ - 2752 (深入理解前缀函数算法:打印字符串既是前缀也是后缀的所有前缀子串位置)
- 9. Blue Jeans POJ - 3080 (暴力水题:求不超过10个长度为60的串的最长公共子串)
- 10. Simpsons’ Hidden Talents HDU - 2594(kmp水题:略了)
- 11. Count the string HDU - 3336(EKMP模板题:求所有前缀出现的次数||所有次数之和)
- 12. Clairewd’s message HDU - 4300 (暂时做不出来可以留着没事想想?但是下次的第一件事情应该就是做这件事情?)
前言
个人总结:字符串学习&总结(感觉主要是总结模板)
- 很久没刷专题了,有点后悔没有坚持下去。现在,把字符串&计算几何&DP都给刷一遍吧,其他的比如数论图论把那种很基础的算法弄的很熟悉就ok了。
- 专题传送门:[kuangbin带你飞]专题1-23
- 感觉直接刷cf上的题单应该能帮助cf快速上分?:codeforces上的一个题单(简化版)
题目(kmp算法我觉得应该称为前缀函数算法)
1. Number Sequence HDU - 1711 (KMP模板题:在另一个字符串中找一个字符串出现的位置)
- 传送门:Number Sequence HDU - 1711
- 题意:字符串->数字(’#’->1e7),然后就是KMP模板了(在另一个字符串中找一个字符串第一次出现的位置)
- 代码:
#include <iostream>
#include <string>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 2e6 + 10;
int n, m;
int a[N], b[N];
//在线算法:即,其实可以一个字符一个字符输入
//与其说是KMP算法,不如说是前缀函数算法。而KMP也只是它的一个应用
int pi[N]; //前缀数组
//得到前缀函数,即前缀数组的值
//注意进入函数的是s还是s+1?
//当然是s,我发现,我之前就,从来没写过s+1的kmp。(之后多刷题,再说吧)
void get(char *s, int l) {
for (int i = 1; i < l; i++) {
int j = pi[i - 1];
while (j > 0 && s[i] != s[j]) j = pi[j - 1];
if (s[i] == s[j]) j++;
pi[i] = j;
}
}
//######################根据题目不同写不同的东西咯:
signed main() {
int T;
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < m; i++) cin >> b[i];
int len = m;
b[len++] = 1e7;
for (int i = 0; i < n; i++) b[len++] = a[i];
//注意是a中找b
for (int i = 1; i < len; i++) {
int j = pi[i - 1];
while (j > 0 && b[i] != b[j]) j = pi[j - 1];
if (b[i] == b[j]) j++;
pi[i] = j;
}
// for (int i = 0; i < len; i++) cout << b[i] << " ";
// cout << endl;
// for (int i = 0; i < len; i++) cout << pi[i] << " ";
// cout << endl;
bool f = false;
for (int i = 0; i < len; i++) {
if (pi[i] == m) {
f = true;
cout << i - 2 * m + 1 << endl;
break;
}
}
if (!f) cout << -1 << endl;
}
return 0;
}
2. Oulipo HDU - 1686 (KMP模板题:求一个子串在另一个子串中出现的次数)
- Oulipo HDU - 1686
- 题意:求一个子串在另一个子串中出现的次数
- 题解&代码:(略)KMP模板题
3. 剪花布条 HDU - 2087 (KMP模板题:求另一个字符串中最多可以在剪出多少个该字符串)
- 剪花布条 HDU - 2087
- 题意:求另一个字符串中最多可以在剪出多少个该字符串
- 题解:kmp模板题,略了
- 代码:略了
4. Cyclic Nacklace HDU - 3746 (前缀函数模板题:求字符串周期n-pi[n-1])
- Cyclic Nacklace HDU - 3746
- 题意:求字符串周期
- 题解:n-pi[n-1]
- 代码:略,但是要注意
- C++就wa,G++就ac
- cin>>就TLE,scanf就TLE(cin>>1e7个数可能确实有点难受。不晓得正式赛的时候,等到TLE再用scanf来不来得及。习惯问题emmm)
5. Period HDU - 1358 (前缀函数模板题:求字符串前缀的周期i+1-pi[i])
- Period HDU - 1358
- 题意&题解&代码:略
6. The Minimum Length HUST - 1010 (求周期水题:略了)
7. Power Strings POJ - 2406 (求周期水题+1,略了略了)
8. Seek the Name, Seek the Fame POJ - 2752 (深入理解前缀函数算法:打印字符串既是前缀也是后缀的所有前缀子串位置)
- Seek the Name, Seek the Fame POJ - 2752
- 题意:打印字符串既是前缀也是后缀的所有前缀子串位置
- 子串位置是起始位置,而且注意打印时的位置我们按下标从1开始
- 题解:
- 求前缀函数的时候按下标从0开始
- 这个题涉及求前缀函数的算法原理
- 代码:
#include <cstring>
#include <iostream>
#include <set>
#include <string>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 4e5 + 10;
char s[N];
int n;
//在线算法:即,其实可以一个字符一个字符输入
//与其说是KMP算法,不如说是前缀函数算法。而KMP也只是它的一个应用
int pi[N]; //前缀数组
//得到前缀函数,即前缀数组的值
//注意进入函数的是s还是s+1?
//当然是s,我发现,我之前就,从来没写过s+1的kmp。(之后多刷题,再说吧)
void get(char *s, int l) {
for (int i = 1; i < l; i++) {
int j = pi[i - 1];
while (j > 0 && s[i] != s[j]) j = pi[j - 1];
if (s[i] == s[j]) j++;
pi[i] = j;
}
}
//######################根据题目不同写不同的东西咯:
void dfs(int p) {
if (!p) return;
dfs(pi[p - 1]);
printf("%d ", p);
}
signed main() {
while (scanf("%s", s) != EOF) {
n = strlen(s);
get(s, n);
dfs(n); //遍历顺序是从大到小,打印顺序是从小到大
puts("");
}
return 0;
}
9. Blue Jeans POJ - 3080 (暴力水题:求不超过10个长度为60的串的最长公共子串)
- Blue Jeans POJ - 3080
- 题意:求不超过10个长度为60的串的最长公共子串,只要求长度大于等于3的子串(如果没有输出
"no significant commonalities"
)。如果长度相等输出字典序最小的串。 - 题解:也许可以用xxx算法,但是能暴力还是暴力emm
- 代码:
#include <iostream>
#include <string>
#define dbg(x) cout << #x << "===" << x << endl;
using namespace std;
const int N = 15;
string s[N];
int n;
signed main() {
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++) cin >> s[i];
string ans, str;
// dbg(ans.size());
// dbg(str.size());
bool f;
for (int len = 3; len <= 60; len++) {
// dbg(len);
for (int i = 0; i + len - 1 < 60; i++) {
str = s[1].substr(i, len); //枚举字符串
// dbg(str);
f = true;
for (int j = 2; j <= n; j++) {
bool f1 = false;
for (int ii = 0; ii + len - 1 < 60; ii++) {
if (s[j].substr(ii, len) == str) {
f1 = true;
break;
}
}
if (!f1) {
f = false;
break;
}
}
if (f) {
// dbg(str);
if (ans.size() < len)
ans = str;
else
ans = min(ans, str);
}
}
}
if (ans.size() < 3)
puts("no significant commonalities");
else
cout << ans << endl;
}
return 0;
}
/*
3
2
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3
GATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATACCAGATA
GATACTAGATACTAGATACTAGATACTAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
GATACCAGATACCAGATACCAGATACCAAAGGAAAGGGAAAAGGGGAAAAAGGGGGAAAA
3
CATCATCATCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
ACATCATCATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AACATCATCATTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
*/
10. Simpsons’ Hidden Talents HDU - 2594(kmp水题:略了)
11. Count the string HDU - 3336(EKMP模板题:求所有前缀出现的次数||所有次数之和)
- Count the string HDU - 3336
- 题意:给一个字符串,求所有前缀出现的次数之和%10007
- 题解:使用EKMP中的z函数,然后求所有z[i]之和,注意
z[0]=n
。- z函数定义:
s[i~n-1]与s的最长公共前缀长度
。 - 比如z[2]=3,则表示
s[2~n-1]
包含了s[0],s[0~1],s[0~2]
这三个前缀
- z函数定义:
- 代码:
#include <iostream>
#include <string>
#define ll long long
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 2e5 + 10;
int n;
char s[N];
//首先约定:字符串下标从0开始
int z[N]; // z[i]定义:s和s[i,n-1]的最长公共前缀的长度。
//叫做z函数,特别的z[0]=0(也不一定,有时候题目就会暗示z[0]=n,注意变通就ok了)
void z_function(char *s, int n) {
int l = 0, r = 0; //算法的过程中我们维护右端点最靠右的匹配段[l,r]
for (int i = 1; i < n; i++) {
//如果i<=r,[i,r]和[i-l,r-l]与s的最长公共前缀应该相等
if (i <= r && z[i - l] < r - i + 1) {
z[i] = z[i - l];
} else {
z[i] = max(0, r - i + 1);
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
}
if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
//注意每次需要O(1)更新一下下
}
z[0] = n; //有时候题目会有这个要求,但是有时候就z[0]=0
}
signed main() {
int T;
cin >> T;
while (T--) {
scanf("%d%s", &n, s);
for (int i = 0; i < n; i++) z[i] = 0;
z_function(s, n);
// for (int i = 0; i < n; i++) cout << ">>>" << i << " " << z[i] <<
// endl;
int ans = 0;
for (int i = 0; i < n; i++) ans = (ans + z[i]) % 10007;
printf("%d\n", ans);
}
return 0;
}
12. Clairewd’s message HDU - 4300 (暂时做不出来可以留着没事想想?但是下次的第一件事情应该就是做这件事情?)
- Clairewd’s message HDU - 4300
- 题意:不超过100个测试样例,每个测试样例有一个长度为26和一个长度不超过1e5的字符串。
- 每个测试样例,第一个字符串26个不同小写字母表示密文,对应明文"abcdefg…",比如"qwertyuiopasdfghjklzxcvbnm",q-a,w-b,…
- 第二个字符为某个情报,只截取了所有密文+部分明文(可能没有明文,也可能是完整明文)。
- 要求求出完整的最小可能长度的"密文+明文"。
- 样例:
- 题解: