标签(空格分隔): 数据结构和算法
串(string)是由零个或多个字符组成的有限序列,又名叫字符串。
一般记为 s = “a1a2a3…an” (n>0)
串可以是空串,即没有字符,直接由""表示(注意里面没有空格,只是双引号),或者可以用希腊字母 Φ 来表示。
子串与主串,例如"HopeStudio" 是 "HopeStudio.com"的子串,反之则倒过来。
BF算法属于朴素的模式匹配算法,它的核心思想是:
在这里S是主串,T是字串,这种子串的定位操作通常称作串的模式匹配。
代码实现
#include
typedef char *String;
//返回子串T在主串S的第pos个字符后(含第pos个位置)第一次出现的位置
//若不存在,则返回-1
//采用BF算法,这里的位置全部以从1开始计算为准,其中T非空,1<=pos<=T[0]
int Index_BF( String S, String T , int pos )
{
int i = pos; //主串当前正待比较的位置,初始为pos
int j = 1; //子串当前正待比较的位置,初始为1
while( i <= S[0] && j <= T[0] )
{
if( S[i] == T[j] ) //如果当前字符相同,则继续向下比较
{
i++;
j++;
}
else //如果当前字符不同,则i和j回退,重新进行匹配
{
i = i-j+2;
j = 1;
}
}
if( j > T[0] )
{
return i - T[0];
}
else
{
return -1;
}
}
int main()
{
char S[255];
char T[255];
char c, e;
int n = 1;
int k = 0;
int pos;
printf("请输入主串: ,并以#作为结束标志!\n");
scanf("%c", &c);
while( c != '#' )
{
S[n++] = c;
S[n] = '\0';
scanf("%c", &c);
}
S[0] = n-1;
printf("请输入子串: ,并以#作为结束标志!\n");
scanf("%c", &e);
while( e != '#' )
{
T[k++] = e;
T[k] = '\0';
scanf("%c", &e);
}
T[0] = k-1;
printf("请输入主串中开始进行匹配的位置(首字符位置为1):\n");
scanf("%d", &pos);
int result = Index_BF( S, T, pos );
if( result != -1 )
{
printf("主串与子串在主串的第%d个字符(首字符的位置为1)处首次匹配", result);
}
else
{
printf("无匹配子串\n");
}
return 0;
}
S | 14 | I | l | o | v | e | F | i | s | h | C | . | c | o | m |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
T | 5 | I | l | o | v | x | |||||||||
j | 0 | 1 | 2 | 3 | 4 | 5 |
S | 13 | w | w | w | . | F | i | s | h | C | . | c | o | m |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
T | 3 | w | w | . | ||||||||||
j | 0 | 1 | 2 | 3 |
S | 12 | b | b | s | b | b | s | . | F | i | s | h | C |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
T | 6 | b | b | s | b | b | c | ||||||
j | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
S | 8 | s | s | s | s | s | s | s | x |
---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
T | 5 | s | s | s | s | b | |||
j | 0 | 1 | 2 | 3 | 4 | 5 |
给模式匹配串添加一个k数组(也就是KMP算法中非著名的next数组)。
这是一个“智能”的数组,因为他指导着模式匹配串下一步该用第几号元素去进行匹配。
思路启发一
S | 14 | I | l | o | v | e | F | i | s | h | C | . | c | o | m |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
T | 5 | I | l | o | v | x | |||||||||
j | 0 | 1 | 2 | 3 | 4 | 5 | |||||||||
k | 0 | 1 | 1 | 1 | 1 |
S | 13 | w | w | w | . | F | i | s | h | C | . | c | o | m |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
T | 3 | w | w | . | ||||||||||
j | 0 | 1 | 2 | 3 | ||||||||||
k | 0 | 1 | 2 |
S | 12 | b | b | s | b | b | s | . | F | i | s | h | C |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
T | 6 | b | b | s | b | b | c | ||||||
j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ||||||
k | 0 | 1 | 2 | 1 | 2 | 3 |
S | 8 | s | s | s | s | s | s | s | x |
---|---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
T | 5 | s | s | s | s | b | |||
j | 0 | 1 | 2 | 3 | 4 | 5 | |||
k | 0 | 1 | 2 | 3 | 4 |
T | 9 | a | b | a | b | a | a | a | b | a |
---|---|---|---|---|---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
next | 0 | 1 | 1 | 2 | 3 | 4 | 2 | 2 | 3 |
i (后缀) = 1 2 。3 4 5 6 7 。8 9
j (前缀) = 0 1 0 1 2 3 4 2 1 2 3
next数组:当模式匹配串T失配的时候,next数组对应的元素指导应该用T串的哪个元素进行下一轮的匹配。
void get_next( String T, int *next )
{
j = 0;
i = 1;
next[1] = 0;
while( i < T[0] )
{
if( 0 == j || T[i] == T[j] )
{
i++;
j++;
next[i] = j;
}
else
{
//j回溯
j = next[j];
}
}
//因为前缀是固定的,后缀是相对的
}
#include
typedef char * String;
void get_next( String T, int *next )
{
int j = 0;
int i = 1;
next[1] = 0;
while( i < T[0] )
{
if( 0 == j || T[i] == T[j] )
{
i++;
j++;
next[i] = j;
}
else
{
//j回溯
j = next[j];
}
}
//因为前缀是固定的,后缀是相对的
}
int main()
{
char str[255] = " ababaaaba";
int next[255];
int i = 1;
str[0] = 9;
get_next( str, next );
for( i=1; i < 10; i++ )
{
printf("%d ", next[i]);
}
return 0;
}
#include
typedef char * String;
void get_next( String T, int *next )
{
int j = 0;
int i = 1;
next[1] = 0;
while( i < T[0] )
{
if( 0 == j || T[i] == T[j] )
{
i++;
j++;
next[i] = j;
}
else
{
//j回溯
j = next[j];
}
}
//因为前缀是固定的,后缀是相对的
}
int main()
{
char T[255];
int next[255];
int i = 1;
int n = 1;
char c;
printf("请输入需要获取next数组的字符串:,并以#作为结束标志!\n");
scanf("%c", &c);
while( c != '#' )
{
T[n++] = c;
T[n] = '\0';
scanf("%c", &c);
}
T[0] = n;
get_next( T, next );
for( i=1; i < T[0]; i++ )
{
printf("%d ", next[i]);
}
return 0;
}
#include
typedef char * String;
void get_next( String T, int *next )
{
int j = 0;
int i = 1;
next[1] = 0;
while( i < T[0] )
{
if( 0 == j || T[i] == T[j] )
{
i++;
j++;
if( T[i] != T[j] )
{
next[i] = j;
}
else
{
next[i] = next[j];
}
}
else
{
//j回溯
j = next[j];
}
}
//因为前缀是固定的,后缀是相对的
}
//返回子串T在主串S第pos个字符的位置
//若不存在,则返回-1
int Index_KMP( String S, String T, int pos )
{
int i = pos;
int j = 1;
int next[255];
get_next( T, next );
while( i <= S[0] && j <= T[0] )
{
if( 0 == j || S[i] == T[j] )
{
i++;
j++;
}
else
{
j = next[j];
}
}
if( j > T[0] )
{
return i - T[0];
printf("%d ", i - T[0]);
}
else
{
return -1;
}
}
int main()
{
char S[255] ;
char T[255] ;
int next[255];
int n = 1;
int k = 0;
int pos;
char c,e;
printf("请输入主串: ,并以#作为结束标志!\n") ;
scanf("%c", &c);
while( c != '#' )
{
S[n++] = c;
S[n] = '\0';
scanf("%c", &c);
}
S[0] = n-1;
printf("S[0]=%d", S[0]);
printf("请输入子串: ,并以#作为结束标志!\n");
scanf("%c", &e);
while( e != '#' )
{
T[k++] = e;
T[k] = '\0';
scanf("%c", &e);
}
T[0] = k-1;
printf("T[0]=%d", T[0]);
printf("请输入主串中开始进行匹配的位置(首字符位置为1):\n");
scanf("%d", &pos);
int result = Index_KMP( S, T, pos );
if( result != -1 )
{
printf("主串与子串在主串的第%d个字符(首字符的位置为1)处首次匹配", result);
}
else
{
printf("无匹配子串\n");
}
return 0;
}