算法题库(更新中)
KMP
1_模板考查
1.https://www.acwing.com/problem/content/833/
题解
题意
自己看
思路
本身就是KMP模板题,看不明白看以下博客
https://www.cnblogs.com/RimekeyBergling/p/16625360.html
#include<bits/stdc++.h>
using namespace std;
const int N = 1000010, M = 1000010;
int n, m;
char p[N], s[M];
int nx[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
//预处理nx
for (int i = 2, j = 0; i <= n; i++)
{
while (j && p[i] != p[j + 1])//当已经匹配到至少一个字符时,查看下一个字符是否匹配;
{
j = nx[j];//如果不匹配,就等于那个没有匹配成功的字符的nx;
}
if (p[i] == p[j + 1])//如果发现字符相同;
{
j++;//查看下一个字符
}
nx[i] = j;//储存nx[i];
}
//KMP匹配
for (int i = 1, j = 0; i <= m; i++)//i枚举s , j枚举p;
{
while (j && s[i] != p[j + 1])//j没有推到终点就无法匹配;
{
j = nx[j];
}
if (s[i] == p[j + 1])//匹配成功
{
j++;//移动到下一位
}
if (j == n)//p完全与s的一部分字符匹配成功
{
cout << i - n << ' ';//匹配成功的下标;
j = nx[j];//移动
}
}
return 0;
}
————————————————————————————————————
2.http://poj.org/problem?id=3461
题解
题意
短字符能在长字符串里匹配几次;
思路
模板,改一下匹配成功后,记录数量即可;
#include<iostream>
#include<string>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
//题目没给字符串长度,这里用char不太好找字符串里有字符的长度,所以用了string;
string p , s;
int nx[1000006];
cin >> p >> s;
int n , m;
int cnt = 0;
n = p.size();
m = s.size();
//建议从下标1开始读入,所以直接再字符串p和s最前面拼接一个无关字符即可;
p = '1' + p;
s = '1' + s;
//模板化的nx预处理
for(int i = 2 , j = 0 ; i <= n ; i++)
{
while(j && p[i] != p[j + 1])
{
j = nx[j];
}
if(p[i] == p[j + 1])
{
j++;
}
nx[i] = j;
}
//同样是模板化的匹配,只是把匹配成功后的行为更改为计数,然后统一打印
for(int i = 1 , j = 0 ; i <= m ; i++)
{
while(j && s[i] != p[j + 1])
{
j = nx[j];
}
if(s[i] == p[j + 1])
{
j++;
}
if(j == n)
{
cnt++;
j = nx[j];
}
}
cout << cnt << endl;
}
return 0;
}
————————————————————————————————————
2_next的使用
1.http://poj.org/problem?id=2752
题解
题意
输入一个字符串,判断这个字符串前缀和后缀是否相同,相同时输出相同部分字符串的长度。
思路
就拿第一个样例
ababcababababcabab ———————— 2 4 9 18
经过next处理后,会发现
原字符串
a b a b c a b a b a b a b c a b a b
next数组
0 0 1 2 0 1 2 3 4 3 4 3 4 5 6 7 8 9
首先原字符串就是个最长前后缀相同的名字,所以会有最长名字为原字符串长度 = 18;
其次找到下标为18的最后一个next[18],说明字符串前9个和后9个的后缀是相同的,所以会有next[18] = 9;
由于已经缩到了1~9的范围,所以依旧倒着找,next[9] = 4;
这次在1~4里找,next[4] = 2;
最后由于next[2] = 0 , 无法继续缩减范围,并且无法得到更多相同的前后缀;
所以对于该字符串,名字最多为2 4 9 18;
//以下代码在poj中请用G++提交,不然cin >> s会CE
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
int mem[400005];
string s;
while(cin >> s)
{
memset(mem , 0 ,sizeof mem);
int next[400005];
int n = s.size();
s = '1' + s;
for(int i = 2 , j = 0 ; i <= n ; i++)
{
while(j && s[i] != s[j + 1])
{
j = next[j];
}
if(s[i] == s[j + 1])
{
j++;
}
next[i] = j;
}
int tmp = n;
while(next[tmp])
{
mem[next[tmp]]++;
tmp = next[tmp];
}
for(int i = 1 ; i <= n ; i++)
{
if(mem[i] > 0)
{
cout << i << ' ';
}
}
cout << n << endl;
}
}
————————————————————————————————————
3_循环节
1.http://poj.org/problem?id=2406
题解
题意
个人感觉原题目的题意不太好理解;
就是求最小子串,然后找到字符串的循环节出现的次数;
思路
因为在kmp里,next表示———如果s第i位(设s[0]为第0位)与p第j位不匹配,则要回到第next[i]位继续与文本串第j位匹配。
那么模式串第1位到next[n]与模式串第n-next[n]位到n位是匹配的。
所以如果n%(n-next[n])== 0,则存在重复连续子串,而最小的字串长度为n-next[n]。
举例理解
原字符串
a b a b c a b a b a b a b c a b a b
next数组
0 0 1 2 0 1 2 3 4 3 4 3 4 5 6 7 8 9
该例子n = 18 , next[n] = 9 , 此时n % (n - next[n]) = 18 % 9 = 0,说明存在重复连续子串;
长度为n - next[n] = 9,次数为n / (n - next[n]) = 2;
#include<iostream>
#include<cstring>
#include<string.h>
using namespace std;
int main()
{
//题目明文要求cin需要加速
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
string s;
while(cin >> s)
{
//特判一下结尾的"."
if(s == ".")
{
return 0;
}
//模板设next
int next[1000006];
int n;
n = s.size();
s = '1' + s;
for(int i = 2 , j = 0 ; i <= n ; i++)
{
while(j && s[i] != s[j + 1])
{
j = next[j];
}
if(s[i] == s[j + 1])
{
j++;
}
next[i] = j;
}
//如果n%(n-next[n])== 0,则存在重复连续子串,而最小的字串长度为n-next[n]。
//n / (n - next[n])则会得到数量
if(n % (n - next[n]) == 0)
{
cout << n / ( n - next[n]) << endl;
}
else
{
cout << 1 << endl;
}
}
}
————————————————————————————————————
2.http://poj.org/problem?id=1961
题解
题意
一个字符串,求这个字符串到第i个字符为止的循环节的次数。
思路
跟上一题思路差不多,唯一不同的就是当字符串里到达第i个字符时,就把它的循环节打印出来;
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int next[1000006];
int cnt = 1;
int n;
cin >> n;
while(1)
{
if(n == 0)
{
return 0;
}
string s;
cin >> s;
s = '1' + s;
memset(next , 0 , sizeof (next));
cout << "Test case #" << cnt << endl;
cnt++;
for(int i = 2 , j = 0 ; i <= n ; i++)
{
while(j && s[i] != s[j + 1])
{
j = next[j];
}
if(s[i] == s[j + 1])
{
j++;
}
next[i] = j;
}
for(int i = 2 ; i <= n ; i++)
{
if(i % (i - next[i]) == 0 && next[i] > 0)
{
cout << i << " " << i / (i - next[i]) << endl;
}
}
cout << endl;
cin >> n;
}
return 0;
}

浙公网安备 33010602011771号