串与队列的应用(C语言)
用KMP算法查找子串在主串中出现的位置与次数,用队列来储存出现的位置与次数并将它们储存在文件中
个人的想法:
a.KMP算法用的next数组而不是nextval数组, 串的第一个位置储存串的长度,从第二个位置开始储存字符串
b.统计其出现的次数:在KMP函数中,如果第一次成功查找到相应的字符串,即子串j = strlen(arr1)(arr1为子串)时,record++并令 j = 1(即回到第一个子串的位置) ,继续对其主串进行遍历,当i > strlen(arr2)(arr2为主串)时,即完成对主串的遍历,此时record的值即为出现的次数
int KMP(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的次数,pos为要开始寻找的位置
{
int record = 0;
int i = pos;
int j = 1;
while(i <= arr2[0] && j <= arr1[0])
{
if(j == 0 || arr1[j] == arr2[i])
{
i++;
j++;
}
else
j = next[j];
if(j > arr1[0]) //若已经遍历完子串,则令j=1重新遍历,并用record++记录出现的次数
{
j = 1; //将字串的位置回溯到第一个继续进行遍历
record++;//成功遍历就加一
}
}
printf("该字符串一共出现了%d次\n",record);
return record;
}
c.统计其出现的位置:KMP1函数返回的是第一个出现相同字符串的位置,若不出现则不进入main函数中的循环,若出现的进入循环,且出现的位置都用pos数组记录,其中其第一个位置记录的是第一次出现相同字符串的位置,通过其判断是否主串中出现相同的字符串,也是判断是否进入循环的依据。经过循环后,其出现的位置均在pos数组中。
int KMP1(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的位置
{
int record = 0;
int i = pos;//i对应主串
int j = 1;//j对应子串
while(i <= arr2[0] && j <= arr1[0])
{
if(j == 0 || arr1[j] == arr2[i])
{
i++;
j++;
}
else
j = next[j];
}
if(j > arr1[0])
return (i-arr1[0]);
else
return -1;
}
//***以下main函数中的部分代码***
case 5: //寻找
record = KMP(T1.arr, T2.arr, 1, next);
pos[0] = KMP1(T1.arr, T2.arr, 1, next);
if(pos[0] == -1)
{
printf("主串中无对应的字串\n");
}
else
{
while(pos[j] != -1 )
{
j++;
pos[j] = KMP1(T1.arr, T2.arr, pos[j-1]+strlen(arr1), next);
}
printf("该子串在主串中出现的位置为%d\n", record);
printf("其出现的位置为:");
for(j = 0 ; j < record ; j++)
{
printf("%d ",pos[j]);
}
printf("\n");
}
break;
d.队列储存思想简述:将pos数组依次赋值到队列中即可
以下是程序的完整代码:
#include <stdio.h>
#include <string.h>
#define OK 1
#define ERROR -1
#define MAXSIZE 200
char *s_gets(char *st, int n)//构造一个去除换行符的输入方法
{
char *find;
char *ret_val;
ret_val = fgets(st, n, stdin);
if(ret_val)
{
find = strchr(st,'\n');
if(find)
*find = '\0';
else
{
while(getchar() != '\n')
continue;
}
}
return ret_val;
}
typedef struct // 定义串的结构
{
char arr[MAXSIZE];
}String;
int StrAssign(String *T ,char *arr) // 赋值
{
int i;
if(strlen(arr) > MAXSIZE)
return ERROR;
else
{
T->arr[0] = strlen(arr);
for(i = 1 ; i <= strlen(arr) ; i++)
{
T->arr[i] = *(arr+i-1);
}
}
return OK;
}
void GetNext(char *arr, int* next) // 求next数组
{
int i = 1; // 从一开始 ,则其最长前后缀的值为各个next数组的值减去一
int j = 0;
next[1] = 0;
while(i < arr[0])
{
if(j == 0 || arr[j] == arr[i]) //j等于0是其回溯的结果;
{
j++;
i++;
next[i] = j;
}
else
j = next[j]; //回溯,寻找其当前最大前后缀的最大前后缀
}
for(i = 1; i < strlen(arr) ; i++) // 打印出next数组
{
printf("next[%d] = %d\n" ,i ,next[i]);
}
printf("已成功给next数组赋值\n");
}
int KMP(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的次数,pos为要开始寻找的位置
{
int record = 0;
int i = pos;
int j = 1;
while(i <= arr2[0] && j <= arr1[0])
{
if(j == 0 || arr1[j] == arr2[i])
{
i++;
j++;
}
else
j = next[j];
if(j > arr1[0]) //若已经遍历完子串,则令j=1重新遍历,并用record++记录出现的次数
{
j = 1; //将字串的位置回溯到第一个继续进行遍历
record++;//成功遍历就加一
}
}
printf("该字符串一共出现了%d次\n",record);
return record;
}
int KMP1(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的位置
{
int record = 0;
int i = pos;//i对应主串
int j = 1;//j对应子串
while(i <= arr2[0] && j <= arr1[0])
{
if(j == 0 || arr1[j] == arr2[i])
{
i++;
j++;
}
else
j = next[j];
}
if(j > arr1[0])
return (i-arr1[0]);
else
return -1;
}
typedef struct{ // 定义队列
int head;
int tail;
int arr[MAXSIZE];
}Queue;
void InitQueue(Queue *p)
{
p->head = 0;
p->tail = 0;
}
void isEmpty(Queue *p) //判断队列是否为空
{
if(p->head == p->tail)
printf("这个队列为空\n");
else
printf("这个队列不为空\n");
}
void isFull(Queue *p) //判断队列是否已满
{
if(p->head == ((p->tail + 1) % MAXSIZE))
printf("此队列已满\n");
else
printf("此队列未满\n");
}
void GetIn_queue(Queue *p, int num) //向队列输入值
{
int i;
i = p->tail;
p->arr[i] = num;
p->tail++;
if((p->tail + 1) % MAXSIZE == p->head)
printf("此队列已满\n");
}
void Print_queue(Queue *p) //打印出队列中的值
{
int i, j;
i = p->head;
j = p->tail;
printf("其出现的次数为 :");
printf("%d\n" , p->arr[i]);
printf("其出现的位置分别是:");
for(i = p->head+1 ; i < j ; i++)
{
printf("%d ", p->arr[i]);
}
printf("\n");
}
int main()
{ //列表
printf("1. 打开文件\n");
printf("2. 赋值\n");
printf("3. 建立next数组\n");
printf("4. 给next数组赋值\n");
printf("5. 寻找\n");
printf("6. 存入队列\n");
printf("7. 打印队列\n");
printf("8. 输入文件\n");
printf("9. 关闭文件\n\n");
printf("输入0退出程序\n");
//声明变量
FILE *q;
FILE *r;
String T1;
String T2;
char arr1[MAXSIZE] = "ezreal";
char arr2[MAXSIZE];
int *next; //创建next数组
int record; //记录出现的次数
int pos[MAXSIZE]; //记录出现的位置
int j = 0;
int choose = -1;
int judge;
Queue p;
while(choose != 0)
{
printf("请选择:\n");
scanf("%d",&choose);
switch(choose)
{
case 1:// 建立
q = fopen("C:\\Ezreal\\Num2\\1.txt", "r");
r = fopen("C:\\Ezreal\\Num2\\2.txt", "w");
if(q && r)
printf("成功打开文件\n");
else
printf("打开文件失败\n");
break;
case 2: //赋值
//printf("请输入子字符串:\n");
//fgets(arr1, MAXSIZE,stdin);//从终端输入子字符串
fgets(arr2, MAXSIZE, q);//从文件输入主字符串
StrAssign(&T1, arr1);//将其字符串数组的第一个值改为字符串的长度
StrAssign(&T2, arr2);
break;
case 3: //建立next数组
next = (int *)malloc(sizeof(int) * strlen(arr1)); //创建next数组
if(next != NULL) // 判断是否创建成功
printf("成功创建next数组\n");
else
printf("创建失败\n");
break;
case 4: //给next数组赋值
GetNext(T1.arr, next);
break;
case 5: //寻找
record = KMP(T1.arr, T2.arr, 1, next);
pos[0] = KMP1(T1.arr, T2.arr, 1, next);
if(pos[0] == -1)
{
printf("主串中无对应的字串\n");
}
else
{
while(pos[j] != -1 )
{
j++;
pos[j] = KMP1(T1.arr, T2.arr, pos[j-1]+strlen(arr1), next);
}
printf("该子串在主串中出现的位置为%d\n", record);
printf("其出现的位置为:");
for(j = 0 ; j < record ; j++)
{
printf("%d ",pos[j]);
}
printf("\n");
}
break;
case 6: //存入队列
InitQueue(&p);
isEmpty(&p);
GetIn_queue(&p, record); // 队列的第一个位置存入出现的次数
for(j = 0 ; j < record ; j++) //之后存入出现的位置
{
GetIn_queue(&p, pos[j]);
}
if(p.head != p.tail)
printf("存入成功\n");
else
printf("存入失败\n");
break;
case 7: //打印队列的内容
Print_queue(&p);
isFull(&p); // 判断是否已满
break;
case 8: //输入到文件
judge = fprintf(r, "该子串在主串中出现的位置为%d\n", record);
fprintf(r,"其出现的位置为:");
for(j = 0 ; j < record ; j++)
{
fprintf(r, "%d ", pos[j]);
}
if(judge > 1) //判断是否成功输入文件
printf("成功输入文件\n");
else
printf("输入文件失败\n");
break;
case 9: //关闭文件
fclose(q);
fclose(r);
break;
default:
if(choose == 0)
printf("成功退出\n");
else
printf("请重新输入数字,");
}
}
}
学艺不精,如有错误,欢迎指出