資料結構(c語言)——串
阿新 • • 發佈:2018-12-19
串的一些個儲存結構:
- 順序儲存結構的串
#define MAXSIZE 255
// 0下標位置的長度存放這個串的長度
typedef unsigned char String[MAXSIZE+1];
- 鏈式儲存的串:
#define MAXSIZE 1024
typedef struct{
char *chars;
//定義一個串長度
int length;
}String;
- 塊鏈式儲存結構的串:一個或若干個字元形成一個塊,並佔用一個節點,串為這樣的節點相連
// 每個塊的大小 #define CHUNKSIZE 128 typedef struct Chunk{ char charArr[CHUNKSIZE]; struct Chunk *next; }Chunk; typedef struct{ Chunk *head,tail; // 當前串長度 int curlen; }String;
本篇博文關於串的操作都是 鏈式儲存結構的。
串的操作
初始化一個串:
String* InitString(){
String *str = (String *)malloc(sizeof(String));
str->chars = (char *)malloc(sizeof(char) * MAXSIZE);
str->length = 0;
return str;
}
字元陣列->串
void charArr2String(String *s,char *string){ int strLength = strlen(string); if(strLength >= MAXSIZE){ s->chars = (char *)malloc(sizeof(char) * MAXSIZE * 2); } // 設定串的當前長度 s->length=strLength; s->chars = string; }
判斷串是否為空
bool StringEmpty(String *str){
return str->length == 0;
}
擷取子串
/* 從s串中指定位置開始取長度為len的字串,用sub返回。 sub:返回的字串 s:主串 pos:指定下標 len:要擷取的長度 */ bool SubString(String *sub,String *s,int pos,int len){ bool flag = pos < 0||pos >= s->length||len < 1||pos + len > s->length; if(flag){ return false; } for(int i = pos; i < (pos + len); i++){ sub->chars[i-pos] = s->chars[i]; } sub->chars[len] = '\0'; sub->length = len; return true; }
比較倆個串的大小
int StringCompare(String *s1,String *s2){
if(StringEmpty(s1)){
return -1;
} else(StringEmp2y(s2)){
return 1;
}
int i=0;
while(i < s1->length && i < s2->length){
if(s1->chars[i] == s2->chars[i]){
i++;
continue;
} else{
return s1->chars[i] - s2->chars[i];
}
}
return 0;
}
在主串s中第pos個字元後查詢與t串相等的字串
int Index(String *s,String *t,int pos){
int i;
String *sub = InitString();
i = pos;
if(pos >= s->length || pos < 0){
return -1;
}
while(i <= s->length - t->length){
SubString(sub,s,pos++,t->length);
if(StringCompare(sub,t) == 0){
return i;
} else{
// 若取出的串不相等,繼續
i++;
}
}
return -1;
}
在主串s中第pos個字元後查詢與t串相等的字串(不考慮用串的其他操作,只用基本的陣列來實現同樣的演算法)
int Index_Arr(String *s,String *t,int pos){
int j = pos;
int i = 0;
if(pos >= s->length || pos < 0){
return -1;
}
while(j < s->length && i< t->length){
if(s->chars[j] == t->chars[i]){
++i;
++j;
} else{
j = j-i+1;
i = 0;
}
}
if(i >= t->length){
return j - t->length;
}
return -1;
}
/*
不足:效率低
看這種情況:
s1:000000000000001
s2:001
串2的前兩個字元,和串1的字元1之前都匹配,需要匹配13*3才可能匹配上,效率低下
所以就有幾個牛人,構思出了一個演算法(KMP演算法),來提高效率。
*/
kmp演算法程式碼實現:獲取模式串的next陣列
/*
通過迴圈,不斷的將字首的單個字元與字尾的單個字元進行比較,
若相等,則將其儲存到陣列中。改變記錄字首和字尾的下標變數。
不相等,將陣列中的值給字首的下標記錄。
*/
void getNextArr(String str,int *nextArr){
// i表示字首的單個字元,j表示字尾的單個字元
int i,j;
i = -1;
j = 0;
nextArr[0] = -1;
// 這個字尾的單個字元的下標是不會回溯的,只有字首的單個字元的下標會進行回溯。
while(j < str.length - 1){
if(i == -1 || str.chars[i] == str.chars[j]){
nextArr[++j] = ++i;
} else {
// 若值不相等,則i值進行回溯
i = nextArr[i];
}
}
}
那麼在使用kmp演算法之後的index應該如何寫呢
int Index_KMP(String S, String P, int pos){
int next[255];
getNextArr(P, next);
int i = pos; // S 的下標
int j = 0; // P 的下標
int s_len = S.length;
int p_len = P.length;
while (i < s_len && j < p_len){
if (j == -1 || S.chars[i] == P.chars[j]){
// P 的第一個字元不匹配或 S[i] == P[j]
i++;
j++;
}
else{
j = next[j];
// 當前字元匹配失敗,退回到合適位置
}
}
if (j == p_len) // 匹配成功
return i - j;
return -1;
}
/*
那麼這個演算法有什麼不足或者說缺陷呢:
在aaaabcdefghijk中找
串aaaaab
當出現不匹配(a和b)的時候,也就是next[j]等於5,
進行回溯,next[j]成了4,不匹配,繼續回溯,直到第next[j]等於0。
b不相等的時候回溯了多次,因為前面5個字元都是相等的,那麼這樣一來之前的判斷回溯就都是多餘的了
*/
改進一下KMP模式匹配演算法
void getNextValArr(String str,int *nextArr){
int i,j;
i = -1;
j = 0;
nextArr[0] = -1;
while(j < str.length - 1){
if(i == -1 || str.chars[i] == str.chars[j]){
// 修改的結果:
if (str.chars[++i] != str.chars[++j]){
// 當前字元和字首字元不同的時候,當前的i為next在j位置的值。
nextArr[j] = i;
} else {
// 字首和當前字元相同將字首字元的nextArr的值賦值給nextArr在i位置的值
nextArr[j] = nextArr[i];
}
} else {
// 若值不相等,則i值進行回溯
i = nextArr[i];
}
}
}
/*
總結改進過的kmp:它是在計算花出next值的同時,
如果a位字元與它next值指向的b位字元相等,則該a位的nextArr就儲存的b位的nextArr值
如果不等,則該a位的nextArr就是它自己a位的nextArr的值
*/
在兩串中找出最大長度的共同串(可指定長度),若存在,返回這個串
void Intersection(String *res,String *s1, String *s2, int len){
if(StringEmpty(s1) ||StringEmpty(s2) || len > s1->length || len > s2->length || len == 0){
return;
}
// 從第i個位置開始拿子串
// 從第j個位置開始找
int i,j;
// 要取的字串長度
int tagetLen = s2->length;
// 儲存s2的子串
String *s2sub = InitString();
// 給子串賦初始值
SubString(s2sub, s2, 0, tagetLen);
while(tagetLen >= len){
i = 0;
while(i <=s2->length - tagetLen){
SubString(s2sub,s2,i,tagetLen);
j = 0;
// 取出字串到s1串中查詢
while(j <= s1->length - s2sub->length){
int index = Index(s1,s2sub,j++);
if(index != -1){
// 找到後進行賦值,然後結束迴圈
res->chars = s2sub->chars;
res->length = tagetLen;
return;
}
}
// 沒找著從下一個位置開始拿子串
i++;
}
// 長度為tagetLen的所有字串在串s1中沒找到,將要取的字串長度自減
tagetLen--;
}
}