Операцию позиционирования подстроки обычно называют сопоставлением строки с образцом.
Наивный алгоритм сопоставления с образцом
- Используйте каждый символ основной строки в качестве начала подстроки, соответствующей строке, подлежащей сопоставлению.
- Создайте большой цикл для основной строки и используйте начало каждого символа в качестве небольшого цикла для длины подстроки до тех пор, пока совпадение не будет успешным или весь обход не будет завершен.
/*返回子串T在主串s中第pos个字符之后的位置。若不存在,则函数返回值为0。*/
/*T非空,1≤pos ≤StrLength(S)。*/
int Index(String S, String T, int pos)
{
int i = pos;/*i用于主串S中当前位置下标,若pos不为1*/
/*则从pos 位置开始匹配*/
int j = 1; /*j用于子串T中当前位置下标值*/
while(i <= S[0] && j <= T[0])/*若i小于S长度且j小于T的长度时循环*/
{
if(S[i] == T[j]) /*两字母相等则继续*/
{
++i;
++j;
}
else /*指针后退重新开始匹配*/
{
i = i-j+2; /*i退回到上次匹配首位的下一位*/
//串是从 1 开始计数的
//i-j+1 恰好回到这次匹配的第一个字符
//所以 (i-j+1)+1 指到下一个字符
//从而开始一轮新的匹配
j = 1;
}
}
if(j > T[0]){
return i-T[0];
}
else
{
return 0;
}
}
Временная сложность: O(n*m)
Это занимает слишком много времени и обычно не рекомендуется.
Алгоритм сопоставления с образцом KMP
Есть три предшественника: DEKnuth, JHMorris и VRPratt (совместные исследования Кнута и Пратта, независимое исследование Морриса) опубликовали алгоритм сопоставления с образцом, который позволяет значительно избежать ситуации повторного обхода, мы называем его Специальным алгоритмом Кнута-Морриса-Пратта , называемым как алгоритм КМП .
В алгоритме KMP нужно менять только положение подстроки вперед и назад, а основная строка перемещается назад.
- Некоторые цифры сопоставляемой строки не равны друг другу и имеют соответствующие подстроки в основной строке. Если последняя цифра не совпадает, эти цифры в сопоставляемой строке не обязательно должны быть явно не равны сами себе. в основной строке символы для сравнения;
- В строке, подлежащей сопоставлению, имеются равенства палиндромов, и они имеют соответствующие подстроки в основной строке. Если последняя цифра не совпадает, первые несколько цифр строки-палиндрома в строке, подлежащей сопоставлению, не обязательно должны быть явно равны сами себе. в основной строке символы для сравнения.
- Значительное сокращение затрат времени,Временная сложность O(n+m);
Найдите позицию возврата подстроки:
/*通过计算返回子串T的next数组。*/
void get_next(String T, int *next)
{
int i,j;
i = 1;
j = 0;
next[1] = 0;
while(i < T[0]) /*此处T[0]表示串T的长度*/
if(j == 0 || T[i] == T[j])/*T[i]表示后缓的单个字符,*/
/* T[j]表示前缀的单个字符 */
{
++i;
++j;
next[i]=j;
}
else
{
j= next[j];/* 若字符不相同,则j值回溯*/
}
}
Главный код
// 返回子串T在主串S中第pos个字符之后的位置。
// 若不存在,则函数返回值为0
// T非空,1<=pos<=StrLength(S)
int Index_KMP(String S, String T, int pos){
// i 用于主串S当前位置下标值,若pos不为1,则从pos位置开始匹配
int i = pos;
int j = 1;//j 用于子串T中当前位置下标值
int next[255];// 定义next数组
get_next(T,next);// 对串T作分析,得到next数组
// 若i小于S的长度且j小于T的长度时,循环继续
while(i <= S[0] && j <= T[0]){
// 两字母相等则继续,相对于朴素算法增加了j=0 判断
if(j == 0 || S[i] == T[j]){
++i;
++j;
}
// 指针后退重新开始匹配
else{
// j退回合适的位置,i值不变
j = next[j];
}
}
if(j>T[0]){
return i-T[0];
}
else
{
return 0;
}
}
Вложение:
Улучшения в поиске следующего массива :
// 求模式串T的next函数修正值并存入数组nextval
void get_nextval(String T,int *nextval){
int i,j;
i = 1;
j = 0;
nextval[1] = 0;
while(i < T[0]){
if(j == 0 || T[i] == T[j]){
i++;
j++;
if(T[i] != T[j])// 若当前字符与前缀字符不同
{
nextval[i] = j;// 则当前的j为nextval在i位置的值
}
else
{
nextval[i] = nextval[j];
//如果与前缀字符相同,则将前缀字符的nextval值赋值给nextval在i位置的值
}
}
else
{
//若字符不相同,则j值回溯
j=nextval[j];
}
}
}
Если вы действительно не можете понять, то запомните!