1. 程式人生 > 其它 >C語言實現多字串搜尋

C語言實現多字串搜尋

技術標籤:C語言+C++c語言演算法

一、問題來源

用C語言程式設計實現在文字檔案Android.log中搜索字串,搜尋“CameraService::connect”與“logicalCameraId: 5, cameraId: 5”,並打印出來包含字串的那一行內容。

字串搜尋是我們經常會遇到的問題,特別是在log中某些資訊定位中起著重要作用。如何在海量的文字資料中,找到我們想要的資訊,對搜尋演算法的速度和精度都有著較高的要求。

二、演算法選擇

字串搜尋演算法有很多種,例如:KMP演算法、正則匹配、暴力查詢、Rabin-Karp演算法、Sunday演算法、BF演算法等。

各個字串搜尋演算法原理詳解與實現參考網址:

link

本文采用了KMP(Knut Morris Pratt)演算法,KMP演算法是一種線性時間複雜度的字串匹配演算法,它是對BF(Brute-Force,最基本的字串匹配演算法)的改進。kmp演算法主要是減少字串查詢過程中的回退,儘可能減少不用的操作,演算法複雜度是O(n+m)。

KMP演算法的整體思路是什麼樣子呢?讓我們來看一組例子:
在這裡插入圖片描述

KMP演算法和BF演算法的“開局”是一樣的,同樣是把主串和模式串的首位對齊,從左到右對逐個字元進行比較。

第一輪,模式串和主串的第一個等長子串比較,發現前5個字元都是匹配的,第6個字元不匹配,是一個“壞字元”:

在這裡插入圖片描述

這時候,如何有效利用已匹配的字首 “GTGTG” 呢?

我們可以發現,在字首“GTGTG”當中,後三個字元“GTG”和前三位字元“GTG”是相同的:
在這裡插入圖片描述

在下一輪的比較時,只有把這兩個相同的片段對齊,才有可能出現匹配。這兩個字串片段,分別叫做最長可匹配字尾子串和最長可匹配字首子串。

第二輪,我們直接把模式串向後移動兩位,讓兩個“GTG”對齊,繼續從剛才主串的壞字元A開始進行比較:
在這裡插入圖片描述

顯然,主串的字元A仍然是壞字元,這時候的匹配字首縮短成了GTG:
在這裡插入圖片描述

按照第一輪的思路,我們來重新確定最長可匹配字尾子串和最長可匹配字首子串:
在這裡插入圖片描述

第三輪,我們再次把模式串向後移動兩位,讓兩個“G”對齊,繼續從剛才主串的壞字元A開始進行比較:
在這裡插入圖片描述

以上就是KMP演算法的整體思路:在已匹配的字首當中尋找到最長可匹配字尾子串和最長可匹配字首子串,在下一輪直接把兩者對齊,從而實現模式串的快速移動。

引用文章連結:link

三、程式實現

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int KMP(char s[],char t[]);
void Getnext(int next[],char t[]);

int main(int argc, char *argv[])
{
	//Read the file
	FILE *fp = fopen("/root/program/Android.log","r");	
	if(fp == NULL)

	{
		printf("Open error!");
		return 0;
	}
	//Read the string that the user wants to look up.
	printf("Please enter the number of strings of you want to find: \n");
	int x ;
	scanf("%d",&x);
	char str[x][100];
	for (int i = 0; i < x; i ++)
	{	
		printf("Please enter a string: \n");
		scanf("%s",&str[i]);
	}

	//Assigns the read to s and outouts it	
	char s[1000];
	int m = 1;	//row
	while(fgets(s,sizeof(s),fp))
	{
		//char t[] = "CameraService::connect";	
		for (int i = 0; i < x; i ++)
		{
			int n = 0;	//column				
			n = KMP(s,str[i]);
			if (n != -1)
			{
				printf("%s is in the %d row %d column \n",str[i],m,n);
				printf("%s \n",s);
			}
		}
		m++;	//row +1
	}
	fclose(fp);	//close file
	return 0;
}

/*
KMP演算法
關鍵在於next,回退位置
*/
int KMP(char s[],char t[])
{
	int i=0,j=0;
	int slen = strlen(s);
	int tlen = strlen(t);
	int next[tlen];					//next陣列長度與字串t等長?
	Getnext(next,t);
	while(i < slen && j < tlen)
	{
		if(j == -1 || s[i] == t[j])
		{
			i++;
			j++;
		}
		else 
		{
			j = next[j];	// j回退
		}
	}
	if (j == tlen)
		return i - j;	//匹配成功,返回子串位置
	else
		return (-1);	//匹配失敗
}

void Getnext(int next[],char t[])
{
	int j = 0,k = -1;
	next[0] = -1;
	int tlen =strlen(t); 
	while(j < tlen -1)
	{
		if (k == -1 || t[j] == t[k])
		{
			j++;
			k++;
			if(t[j] == t[k])	//當兩個字元相同時,就跳過
				next[j] = next[k];
			else
				next[j] = k;
		}
		else
		{
			k = next[k];
		}
	}

}