DS串应用--KMP算法
输入
第一个输入t,表示有t个实例
第二行输入第1个实例的主串,第三行输入第1个实例的模式串
以此类推
输出
第一行输出第1个实例的模式串的next值
第二行输出第1个实例的匹配位置,位置从1开始计算,如果匹配成功输出位置,匹配失败输出0
以此类推
输入样例1
3
qwertyuiop
tyu
aabbccdd
ccc
aaaabababac
abac
输出样例1-1 0 0
5
-1 0 1
0
-1 0 0 1
8
#include<iostream> #include<string> using namespace std; //KMP算法的核心是利用匹配失败后的信息, //尽量减少模式串与主串的匹配次数以达到快速匹配的目的。 //具体实现就是通过一个next()函数实现, class mystring { private: string mainstr; int size; //求得next数组 void getnext(string p, int next[]) { int j = 0, k = -1; //getbext中 k表示最大相等长度 初值设为-1 当前字符没有前后相等字串 next[0] = -1;//next默认值为-1 因为一个字符串无最大前后相等字串 int len = p.length(); while (j < len - 1) //找next j最大len-2 { if (k == -1 || p[k] == p[j] ) //k为-1 或者 匹配字符 { j++; k++; //检查下一个字符 最大相等长度+1 next[j] = k; //next[j] = k, 将最大相等长度存入next数组 } else { k = next[k]; //匹配失败位置,找到他前面的字符串的最大前后相等子串长度K } } } //kmp算法 找到匹配字串的首字符 int kmpfind(string p, int pos, int next[]) //从pos位置开始查找字串 { int j= pos, k = 0; //kmpfind中 k表示模式串当前正在匹配的位置 从0开始 int len = p.length(); //子串长度 while (j < size && k < len) //pos位置要少于主串长度 { if (k == -1 || p[k] == mainstr[j]) //匹配主串第pos位置的字符 { j++; k++; //匹配下一个字符 } else { k = next[k]; } } if (k == len) return j - len + 1; //子串走到尽头,返回开始匹配时的位置 return 0; //不匹配标志 } public: mystring() { size = 0; mainstr = ""; } ~mystring() { size = 0; mainstr = ""; } void setval(string sp) { mainstr = sp; size = mainstr.length(); } int kmpfindsubstr(string p, int pos) { int l = p.length(); int* next = new int[l]; getnext(p, next); //得到next值并输出 for (int i = 0; i < l; i++) { cout << next[i] << ' '; } cout << endl; int v = -1; v = kmpfind(p, pos, next); delete[]next; return v; } }; int main() { int t; cin >> t; while (t--) { mystring str; string sp, p; int pos = 0; cin >> sp >> p; //输入主串字串 str.setval(sp); cout << str.kmpfindsubstr(p, pos) << endl; //从pos位置开始查找字串 } }
DS串应用--串替换
题目描述
给出主串、模式串、替换串,用KMP算法找出模式串在主串的位置,然后用替换串的字符替换掉模式串
本题只考虑一处替换的情况,如果你想做的完美一些,能够实现多处替换那
可能需要考虑模式串和替换串长度不一致的情况
输入
第一个输入t,表示有t个实例
第二行输入第1个实例的主串,第三行输入第1个实例的模式串,第四行输入第1个实例的替换串
以此类推
输出
第一行输出第1个实例的主串
第二行输出第1个实例的主串替换后结果,如果没有发生替换就输出主串原来的内容。
以此类推
输入样例1
3
aabbccdd
bb
ff
aaabbbccc
ddd
eee
abcdef
abc
ccccc
输出样例1aabbccdd
aaffccdd
aaabbbccc
aaabbbccc
abcdef
cccccdef
#include<iostream> #include<string> using namespace std; class myString { private: string mainstr; int size; void getnext(string p, int next[]) { int j = 0, k = -1; next[0] = -1; int len = p.length(); while (j < len - 1) { if (k == -1 || p[j] == p[k]) { j++; k++; next[j] = k; } else { k = next[k]; } } } int kmpfind(string p, int pos, int next[]) //从pos位置开始查找字串 { int j = pos, k = 0; //kmpfind中 k表示模式串当前正在匹配的位置 从0开始 int len = p.length(); //子串长度 while (j < size && k < len) //pos位置要少于主串长度 { if (k == -1 || p[k] == mainstr[j]) //匹配主串第pos位置的字符 { j++; k++; //匹配下一个字符 } else { k = next[k]; } } if (k == len) return j - len ; //子串走到尽头,返回开始匹配时的位置 return -1; //不匹配标志 } public: myString() { size = 0; mainstr = ""; } ~myString() { size = 0; mainstr = ""; } void setval(string sp) { mainstr = sp; size = mainstr.length(); } int kmpfind1(string p, int pos) { int L = p.length(); int* next = new int[L]; getnext(p, next); int v = -1; v = kmpfind(p, pos, next); delete[] next; return v; } }; int main() { int t; cin >> t; string sp, p, change; while (t--) { myString str; cin >> sp >> p >> change; str.setval(sp); cout << sp << endl; int v = str.kmpfind1(p, 0); int l = p.length(); if (v >= 0) { sp.replace(v, l, change); //用change替换指定字符串从起始位置v开始长度为l的字符 } cout << sp << endl; } return 0; }
串应用- 计算一个串的最长的真前后缀
题目描述
给定一个串,如ABCDAB,则 ABCDAB的真前缀有:{ A, AB,ABC, ABCD, ABCDA } ABCDAB的真后缀有:{ B, AB,DAB, CDAB, BCDAB } 因此,该串的真前缀和真后缀中最长的相等串为AB,我们称之为该串的“最长的真前后缀”。 试实现一个函数string matched_Prefix_Postfix(string str),得到输入串str的最长的真前后缀。若不存在最长的真前后缀则输出empty
输入
第1行:串的个数 n 第2行到第n+1行:n个字符串
输出
n个最长的真前后缀,若不存在最长的真前后缀则输出empty。
输入样例1
6
a
ab
abc
abcd
abcda
abcdab输出样例1
empty
empty
empty
empty
a
ab#include<iostream> #include<string> using namespace std; int *getnext(string str) { int j = 0, k = -1; int len = str.length(); int* next = new int[len + 1]; next[0] = -1; while (j < len) { if (k == -1 || str[k] == str[j]) { j++; k++; next[j] = k; } else { k = next[k]; } } return next; } void matched(string str) { int* next = getnext(str); int len = str.length(); int max= next[len]; //查看next数组的最后一个元素(最大公共前后缀) 赋值给ans if (max <= 0) { cout << "empty" << endl; } else { cout << str.substr(0, max) << endl;//截取从0到长度max的字符 } } int main() { int t; cin >> t; while (t--) { string str; cin >> str; matched(str); } }
DS串应用—最长重复子串
题目描述
求串的最长重复子串长度(子串不重叠)。例如:abcaefabcabc的最长重复子串是串abca,长度为4。
输入
测试次数t
t个测试串
输出
对每个测试串,输出最长重复子串长度,若没有重复子串,输出-1.
输入样例1
3
abcaefabcabc
0szu0123szu1
szuabcefg
输出样例14
3
-1#include<iostream> #include<cstring> using namespace std; int* getnext(string str) //返回指针而不是数组 { int j = 0, k = -1; int len = str.length(); int* next = new int[len + 1]; //指针new出来 长度+1 next[0] = -1; while (j < len) //指针的话就是<len { if (k == -1 || (str[j] == str[k])) { j++; k++; next[j] = k; } else { k = next[k]; } } return next; } int rep(string str) { int len = str.length(); int max = 0; string* fun = new string[len + 1]; //存储从字符串中截取的子串 for (int i = 0; i < len; i++) { fun[i] = str.substr(i, len); } for (int i = 0; i < len; i++) //遍历fun中的每个字串 获取每个字串的next值 { int* next = getnext(fun[i]); int len = fun[i].length(); //遍历next数组 找到最大的next for (int j = 0; j < len; j++) { if (max < next[j]) { max = next[j]; } } } if (max == 0) { return -1; } else if (max >= str.length() / 2)//如果最大长度超过了匹配长度的一半 意味着字符串前半部分和后半部分有重叠部分 { return max / 2; //避免重复计算 /2 表示重叠部分长度 } else { return max; } } int main() { int t; cin >> t; while (t--) { string str; cin >> str; cout << rep(str) << endl; } return 0; }