一、学习记录
犹记当年学习C语言的字符串的时候那个悲情。。。
于是组队之后果断声明:鶸不玩字符串
然后将字符串丢给队友。。。
后来觉得吧,一些基础的东西还是要会的,比如KMP
粗浅的学了下KMP算法,也不敢瞎说什么,主要整理了下模板
学习记录:
主要看了刘大大的蓝书《算法竞赛入门经典--训练指南》
还有几篇博客:
1.
KMP算法详解
2.
KMP算法详解
3.
KMP算法的一些题目
二、两种模板
初期使用的模板:
#define mem(a,x) memset(a,x,sizeof(a))
#include
using namespace std;
typedef long long ll;
const int N = 100000;
/*
KMP 算法模板
时间复杂度 : O(n+m) [其中 n,m 是两字符串的长度]
说明:
1. a是长串 s是短串 即 a.size >= s.size
2.实现了“查找第一次匹配的位置” “匹配的次数” “记录所有匹配区间”三个功能
3.三个功能的实现代码非常相似,第3个功能可以同时实现前2个的功能
*/
struct KMP
{
int f[N + 7];
void getfill (string s) //预处理
{
memset (f, 0, sizeof (f) ); //根据其前一个字母得到
for (int i = 1; i < s.size(); i++)
{
int j = f[i];
while (j && s[i] != s[j])
j = f[j];
f[i + 1] = (s[i] == s[j]) ? j + 1 : 0;
}
}
int finds (string a, string s) //找到第一次匹配的位置 a.size >= s.size
{
getfill (s);
int j = 0;
for (int i = 0; i < a.size(); i++)
{
while (j && a[i] != s[j]) j = f[j];
if (a[i] == s[j]) j++;
if (j == s.size() )
{
return i - s.size() + 1; //返回小串在大串中第一次出现的位置
}
}
return -1;//不匹配返回-1
}
int findtimes(string a,string s)//返回匹配次数
{
getfill(s);
int j = 0;int times = 0;
for (int i = 0;i < a.size();++i)
{
while (j&&a[i] != s[j]) j = f[j];
if (a[i] ==s[j]) j++;
if (j == s.size())
{
times++;
}
}
return times;//不匹配的times将是0
}
struct Interval
{
int l,r;
}q[N];//记录所有匹配区间 [指在长串中的下标,下标从0开始]
int t;//记录匹配次数
void findinterval(string a,string s)//找到并记录所有的匹配区间
{
getfill(s);
int j = 0;t = 0;
for (int i = 0;i < a.size();++i)
{
while (j&&a[i]!=s[j]) j = f[j];
if (a[i] == s[j]) j++;
if (j == s.size())
{
t++;
q[t].l = i - s.size() + 1;
q[t].r = i;
}
}
}
void Print()//第3种功能附带打印功能
{
cout<<"两串匹配了 "<< t <<"次,分别是 :"<> a >> s)
{
kmp.findinterval(a,s);
kmp.Print();
}
return 0;
}
后期的模板:
#define mem(a,x) memset(a,x,sizeof(a))
#include
using namespace std;
typedef long long ll;
const int N = 1000007;
struct KMP
{
string s, t; //s.size > t.size
int slen, tlen;
int nx[N];
KMP (string s = "", string t = "") : s (s), t (t) //构造函数
{
slen = s.size();
tlen = t.size();
mem (nx, 0);
}
void init (string s, string t)
{
this->s = s;
this->t = t;
slen = s.size();
tlen = t.size();
mem (nx, 0);
}
void getnx()
{
int i = 0, k = -1;
nx[0] = -1;
while (i < tlen)
{
if (k == -1 || t[i] == t[k]) nx[++i] = ++k;
else k = nx[k];
}
}
int findindex() //返回首次出现的位置
{
int i = 0, j = 0;
getnx();
while (i < slen&&j < tlen)
{
if (j == -1||s[i] == t[j]) ++i,++j;
else j = nx[j];
}
if (j == tlen) return i - tlen;
else return -1;//不匹配
}
int findtimes()//返回出现次数
{
if (slen == 1&&tlen == 1)
{
return s[0] == t[0];
}
getnx();
int times = 0;
for (int i = 0,j = 0;i < slen;++i)
{
while (j > 0&&s[i]!=s[j]) j = nx[j];
if (s[i] == t[j]) ++j;
if (j == tlen)
{
++times;
j = nx[j];
}
}
return times;
}
};
int main()
{
return 0;
}
三、关于next的感受心得
个人以为,KMP的精髓就在伟大的getnext的预处理里,(或者说在神奇的next数组里)
getnext是对模式串进行预处理,而完全不涉及主串
getnext中其实是字符串自己和自己匹配,
关于next数组,他的意义不限于KMP算法,他可以找到字符串的循环节,他有很多独特的性质(很有用)
这里随意测试了2个性质
#define mem(a,x) memset(a,x,sizeof(a))
#include
#include
#include
#include
#include
#include
#include
#include
#include