最近在复习数据结构,看书的时候大多都是伪代码看着难受,数据结构这种东西还是要自己动手才可以真正理解(即是是参考别人的),下面给出可运行的串头文件和KMP实现代码。
串的接口在数据结构书上已经有了,下面是String.h,用数组实现的串结构,第一个位置存放串的长度。
/**
* 下面代码中S指原串 T指目标串
* */
#include
#include
#include
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100
typedef int Status;
typedef int ElemType;
//这里没基础的同学可以自己去网上研究一下 就是把String表示为char[MAXSIZE + 1],所以最长255(char最大值-1)
typedef char String[MAXSIZE + 1];
/**
* 生成一个与chars等值的串T
* */
Status StrAssign(String T, char *chars) {
if(strlen(chars) > MAXSIZE) {
return ERROR;
}
//第一个位置存储长度
T[0] = strlen(chars);
for(int i = 1; i <= T[0]; i++) {
T[i] = *(chars+i-1);
}
return OK;
}
/**
* 由S复制得T串
* */
Status StrCopy(String T, String S) {
for(int i = 0; i <= S[0]; i++) {
T[i] = S[i];
}
return OK;
}
/**
* 判断字符串是否为空
* */
Status StrEmpty(String S) {
return S[0] == 0? TRUE:FALSE;
}
/**
* 字符串比较
* */
int StrCompare(String S, String T) {
for(int i = 1; i <= S[0] && i <= T[0]; i++) {
if(S[i] != T[i]) {
return S[i] - T[i];
}
}
return S[0] - T[0];
}
int StrLength(String S) {
return S[0];
}
Status ClearString(String S) {
S[0] = 0;
return OK;
}
/**
* 字符串拼接 如果没截断(拼接后长度小于MAXSIZE)返回TRUE 否则返回FALSE并截断S2
* */
Status StrConcat(String T, String S1, String S2) {
if(S1[0] + S2[0] <= MAXSIZE) {
for(int i = 1; i <= S1[0]; i++) {
T[i] = S1[i];
}
for(int i = 1; i <= S2[0]; i++) {
T[i+S1[0]] = S2[i];
}
T[0] = S1[0] + S2[0];
return TRUE;
} else {
for(int i = 1; i <= S1[0]; i++) {
T[i] = S1[i];
}
for(int i = 1; i <= MAXSIZE-S1[0]; i++) {
T[i+S1[0]] = S2[i];
}
T[0] = MAXSIZE;
return FALSE;
}
}
/**
* 用SUb返回S串从第pos个字符起始的长度为len的子串
* */
Status SubString(String Sub, String S, int pos, int len) {
if(pos < 1 || pos > S[0] || len < 0 || len > S[0]-pos+1) {
return ERROR;
}
for(int i = 1; i <= len; i++) {
Sub[i] = S[pos+i-1];
}
Sub[0] = len;
return OK;
}
void StrPrint(String T) {
for(int i = 1; i <= T[0]; i++) {
printf("%c", T[i]);
}
}
/**
* 朴素匹配算法
* */
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退回到上次匹配首位的下一位 */
j = 1; /* j退回到子串T的首位 */
}
}
if (j > T[0])
return i-T[0];
else
return 0;
}
Status StrInsert(String S, int pos, String T)
{
if(pos < 1||pos > S[0]+1)
return ERROR;
if(S[0]+T[0] <= MAXSIZE)
{ /* 完全插入 */
for(int i = S[0]; i >= pos; i--)
S[i+T[0]]=S[i];
for(int i = pos; i < pos+T[0]; i++)
S[i]=T[i-pos+1];
S[0]=S[0] + T[0];
return TRUE;
}
else
{ /* 部分插入 */
for(int i = MAXSIZE;i <= pos;i--)
S[i] = S[i-T[0]];
for(int i = pos;i < pos+T[0]; i++)
S[i] = T[i-pos+1];
S[0] = MAXSIZE;
return FALSE;
}
}
Status StrDelete(String S, int pos, int len)
{
if(pos < 1||pos > S[0]-len+1||len < 0)
return ERROR;
for(int i = pos+len; i <= S[0]; i++)
S[i-len] = S[i];
S[0] -= len;
return OK;
}
//用V替换主串S中出现的所有与T相等的不重叠的子串
Status Replace(String S, String T, String V)
{
int i = 1; /* 从串S的第一个字符起查找串T */
if(StrEmpty(T)) /* T是空串 */
return ERROR;
do
{
i=Index(S,T,i); /* 结果i为从上一个i之后找到的子串T的位置 */
if(i) /* 串S中存在串T */
{
StrDelete(S,i,StrLength(T)); /* 删除该串T */
StrInsert(S,i,V); /* 在原串T的位置插入串V */
i += StrLength(V); /* 在插入的串V后面继续查找串T */
}
} while(i);
return OK;
}
KMP算法的介绍在数据结构的书已经写得很详细了,大家可以认真看看。下面是串的应用以及匹配算法代码,传统的匹配算法在String.h里面已经写了,所以下面只有KMP以及KMP的改进匹配算法的代码。
#include "String.h"
void get_next(String T, int *next) {
int i = 1, j = 0;
next[1] = 0;
while(i < T[0]) {
if(j == 0 || T[i] == T[j]) {//T[i]表示后缀的单个字符 T[j]表示前缀的单个字符
i++;
j++;
next[i] = j;
} else {
j = next[j]; //回溯
}
}
}
int Index_KMP(String S, String T, int pos) {
int i = pos, j = 1, next[255];
get_next(T, next);
while(i <= S[0] && j <= T[0]) {
if(j == 0 || S[i] == T[j]) {
i++;
j++;
} else {
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 = 1,j = 0;
nextval[1]=0;
while (i < T[0])
{
if(j == 0 || T[i] == T[j]) /* 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= nextval[j];
}
}
}
//改进后的KMP
int Index_KMP1(String S, String T, int pos)
{
int i = pos, j = 1, next[255];
get_nextval(T, next);
while (i <= S[0] && j <= T[0])
{
if (j == 0 || S[i] == T[j])
{
++i;
++j;
}
else
j = next[j];
}
if (j > T[0]) {
return i-T[0];
}
else {
return 0;
}
}
int main(int argc, char const **argv) {
String S, T, S1, S2;
char s[10] = "aaaaaaab", t[10] = "a", v[10] = "ab";
StrAssign(S, s);
StrAssign(S1, t);
StrAssign(S2, v);
StrConcat(T, S1, S2);
StrInsert(T, 1, S1);
printf("%d %d %d", Index(S, T, 1), Index_KMP(S, T, 1), Index_KMP1(S, T, 1));
return 0;
}