KMP匹配模式
勇敢🐂🐂,不怕困难
关键在于求next数组
时间复杂度为O(m+n),空间复杂度记为O(m)
主要思想:以空间换时间,减少回溯次数
- next[i]=j,含义是:下标为i 的字符串最长相等前后缀的长度为j,且A[i-j+1i]=A[1j]。求解next数组有点动态规划的思想
- 每一个字符前的字符串都有最长相等前后缀,而且最长相等前后缀的长度是我们移位的关键可以理解为模式串在文本串下面移动
- 引理1.若j0是next[i]的一个候选项,即j0<i且A [i-j0+1~i] = A [1~j0],则 < j0的最大的next[i]的候选项是next[j0]
- 引理2.对于字符串s,s[1~i]具有长度为len< i的循环元<=>len能整除i且s[1~i-len] = s[len+1~i]【即i-len是next[i]的候选项】
void getnext(char pattern[], ll nex[])
{
int i = 0, j = -1, len = n;
nex[0] = -1; //next有冲突,要用Next,使得Next数组整体向右移动了一位,这样的话,len=Next[len-1]变为len=Next[len]
while (i < len) //注意不是i<len-1
{
if (j == -1 || pattern[i] == pattern[j])
{
i++, j++;//i表示当前位置,j表示最长公共前缀子串的后一个字符
nex[i] = j;
}
else
j = nex[j];//回溯
}
}
int kmp(char text[], char pattern[])//KMP算法,模式串、文本串
{
int lent = strlen(text), lenp = strlen(pattern);
int i = 0, j = 0;//i、j均初始化为0
get_next(pattern, Next);//记得用Next数组
while (i <= lent)//注意不是i<lent&&j<lenp
{
if (j == -1 || text[i] == pattern[j])
{
i++;
j++;
}
else
j = Next[j];//回溯过程,i不变,j后退
}
if (j >= lenp)
return i - lenp;//返回匹配模式下的首字母下标
else
return -1;//返回-1表示未匹配
}
优化后,但是不建议使用
char pattern[N], text[N];
int Nextval[N];
void getnextval(char pattern[], int Nextval[]) //构造Nextval数组
{
int i = 0, j = -1, len = strlen(pattern); //初始化i=0、j=-1
Nextval[0] = -1;
while (i < len) //注意不是i<len-1
{
if (j == -1 || pattern[i] == pattern[j])
{
i++; //i表示当前位置
j++; //j表示最长公共前缀子串的后一个字符
if (pattern[i] != pattern[j])
Nextval[i] = j;
else
Nextval[i] = Nextval[j];//即字符不匹配时回溯两层后对应的字符的下标
}
else
j = Nextval[j]; //回溯
}
}
int kmp(char text[], char pattern[]) //KMP算法,模式串、文本串
{
int lent = strlen(text), lenp = strlen(pattern);
int i = 0, j = 0; //i、j均初始化为0
getnextval(pattern, Nextval); //记得用Nextval数组
while (i < lent) //注意不是i<lent&&j<lenp
{
if (j == -1 || text[i] == pattern[j])
{
i++;
j++;
}
else
j = Nextval[j]; //回溯过程,i不变,j后退
}
if (j >= lenp)
return i - lenp; //返回匹配模式下的首字母下标
else
return -1; //返回-1表示未匹配
}
最小表示法
- 该算法通过两个指针不断向后移动,尝试比较每个循环同构串的大小,及时排除不可能的选项,当一个移动到结尾时,就考虑了所有可能的二元组(b[i],b[j]),从而得到了最小表示【O(n)】
- 引理:b[i]表示从i开始的循环同构字符串,即b[i]=s [i ~ n]+s[1~i-1],若b[i],b[j]在k处不等,假设s[i+k]>s[j+k],则b[i],b[i+1]…b[i+k]都不是最小表示,因为它们最终也会经过s[i+k]>s[j+k]
int Get_min()
{
int n = strlen(s);//字符串从下标0开始
int i = 0, j = 1, k = 0, t;
while (i < n && j < n && k < n) //表示从i开始k长度和从j开始k长度的字符串相同
{
t = s[(i + k) % n] - s[(j + k) % n]; //t用来计算相对应位置上那个字典序较大
if (!t)
k++; //字符相等的情况
else
{
if (t > 0)
i += k + 1; //i位置大,最大表示法: j += k+1
else
j += k + 1; //j位置大,最大表示法: i += k+1
if (i == j)
j++;
k = 0;
}
}
return min(i, j);
}
总结
QAQ菜🐕