其實串這塊還是很簡單的,主要是kmp算法讓人頭大。考研書上基本都是c語言struct寫的,個人感覺還是用類寫比較清楚一些。
串的結構定義
定長順序存儲表示
給串尾加上'\0'作爲串結束的標記。同時也設定length。這樣做雖然多用一個單位的存儲空間,但就是代碼中用起來最方便的形式。
const int maxSize=99;
class Str{
public:
char str[maxSize+1];
int length;
};
串的動態分配存儲表示和重新賦值
#include <iostream>
using namespace std;
const int maxSize=99;
class Str{
public:
char *ch;
int length=0;
Str(char *c){
ch=c;
while(*c){
++length;
++c;
}
}
int reassign(char* c){
if(ch){
ch=NULL;
length=0;
int len=0;
char *temp;
temp=c;
while(*temp){
++len;
++temp;
}
if(len==0){ // 新字符串爲空串
ch=NULL;
}else{
ch=c;
length=len;
}
return 1;
}
return 0;
}
};
int main(){
Str s("input");
s.reassign("hello world!!");
cout<<s.length; // 13
return 0;
}
串比較
有兩個串a,b,如果a的ASCII碼小於b的ASCII碼,則返回a小於b標記,如果a的ASCII碼大於b的ASCII碼,返回a大於b的標記,如果a等於b的ASCII碼,則按照之前的規則繼續比較兩串中的下一對字符。經過上述步驟,在沒有比較出a和b大小的情況下,先結束的串爲較小串。兩串同時結束,只要返回兩串相等標記。
int strCompare(Str s1,Str s2){
int i=0;
while(i<s1.length&&i<s2.length){
if(s1.ch[i]!=s2.ch[i]){
return s1.ch[i]-s2.ch[i];
}
i++;
}
return s1.length-s2.length;
}
int main(){
Str s("input1");
Str s1("input1");
int res=strCompare(s,s1);
cout<<res; // 0
}
串連接
如果a的ASCII碼小於b的ASCII碼,則返回a小於b標記,如果a的ASCII碼大於b的ASCII碼,返回a大於b的標記,如果a等於b的ASCII碼,則按照之前的規則繼續比較兩串中的下一對字符。經過上述步驟,在沒有比較出a和b大小的情況下,先結束的串爲較小串。兩串同時結束,只要返回兩串相等標記。
int concat(Str &target,Str s1,Str s2){
if(target.ch){
target.ch=NULL;
}
target.ch=new char;
int i=0;
while(i<s1.length){
target.ch[i]=s1.ch[i];
i++;
}
int j=0;
while(j<s2.length){
target.ch[i+j]=s2.ch[j];
j++;
}
target.length=s1.length+s2.length;
return 1;
}
int main(){
Str s1("hello");
Str s2(" world");
Str target("");
concat(target,s1,s2);
cout<<target.length<<endl; // 11
}
求子串
從給定串中某一位置開始到某一位置結束的串的操作稱爲求子串操作。
int Str::substring(Str &str,int pos,int len){
if(str.ch){
str.ch=NULL;
}
if(pos+len>length){
cout<<"所求位置的字符串長度溢出"<<endl;
return 0;
}
str.ch=new char;
int i=0,j=0;
while(i<length){
if(i>=pos&&j<len){
str.ch[j]=ch[i];
j++;
}
i++;
}
str.length=len;
return 0;
}
int main(){
Str s1("hello");
Str s2(" world");
Str target("");
s1.substring(target,0,4);
target.readStr(); // hell
cout<<target.length<<endl; // 4
}
清空串
int Str::clearstring(){
if(ch){
ch=NULL;
}
length=0;
return 1;
}
int main(){
Str s1("hello");
s1.readStr(); //hello
s1.clearstring();
s1.readStr(); //
cout<<endl<<s1.length<<endl; // 0
}
串的模式匹配算法
1.簡單模式匹配的算法
基本思想從主串的第一個位置起和模式串的第一個字符開始比較,如果相等,則繼續逐一比較後續字符。否則從主串兒的第二個字符開始,在重新用上一步的方法與模式串中的字符作比較,以此類推,直到比較完成模式串中所有的字符,若匹配成功,則返回串在主串中的位置,若匹配不成功,則返回一個可區別於主串所有位置的標記,如0。
int Str::indexOf(Str target){
if(length<target.length){
cout<<"invalid arguments"<<endl;
return -1;
}
int i=0,j=0,k=0;
while(i<length&&j<target.length){
if(ch[i]==target.ch[j]){
i++;
j++;
}else{
j=0;
i=++k;
}
}
if(j==target.length){
return k;
}
return -1;
}
int main(){
Str s1("hello");
cout<<s1.indexOf("8")<<endl; //-1
cout<<s1.indexOf("hel")<<endl; // 0
cout<<s1.indexOf("lo")<<endl; //3
}
2.kmp算法
kmp算法不好理解,總之重點是求出next數組,然後套用簡單匹配模式算法,將j的下一跳置爲next[j]就ok了。具體的算法看博客是不好理解的,推薦大家去看視頻。我看的是b站一個印度小哥講解的,汪都能聽懂的KMP字符串匹配算法【雙語字幕】,他的next數組與嚴蔚敏講解的不同,不過很好理解,我是看書真的看不懂了,(我用19天勤的參考書,跑了遍程序發現是錯的ORZ)。下面的算法是拿js實現的,按照小哥思路寫的,和書上的next不一樣,需要你右移一位,每位加1即爲嚴的版本next。
求next數組
function getNextArr(str) {
let j = 0, i = 1;
const next = [0]
let isSuccessiveEqual = true;
while (i < str.length && j < str.length) {
if (str[j] == str[i]) {
next[i] = isSuccessiveEqual ? (next[i - 1] + 1) : next[j] + 1
i++
j++
isSuccessiveEqual = true
} else {
isSuccessiveEqual = false
if (j == 0) {
next[i] = 0
i++
} else {
j = next[j - 1]
}
}
}
return next
}
getNextArr('abcaby') // [0, 0, 0, 1, 2, 0]
getNextArr('aabaabaaa') // [0, 1, 0, 1, 2, 3, 4, 5, 2]
kmp算法
function kmp(text, pattern) {
const next = getNextArr(pattern)
let i = 0, j = 0
while (i < text.length && j < pattern.length) {
if (text[i] == pattern[j]) {
i++
j++
if (j == pattern.length) {
return i - pattern.length
}
} else {
j > 0 ? j = next[j - 1] : i++
}
}
return -1
}
kmp('abxabcabcaby', 'abcaby') // 6
kmp('111str111', 'str11') // 3