串的经典实现代码(包含KMP算法的实现)

最近在复习数据结构,看书的时候大多都是伪代码看着难受,数据结构这种东西还是要自己动手才可以真正理解(即是是参考别人的),下面给出可运行的串头文件和KMP实现代码。

1.串

  串的接口在数据结构书上已经有了,下面是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;
}

2.串的匹配算法

  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;
}

你可能感兴趣的:(数据结构,数据结构)