前言:
串就是我們所說的字符串,計算機對數據的處理主要包括數值計算和非數值計算,在計算機發展的早期,數值計算比較普遍,然而隨着社會的發展和計算需求的發展,在計算機上做非數值處理的計算越來越多,而在計算機上非數值處理大部分是對字符串的處理,處理這些字符串數據比起處理整型、浮點型數據都要複雜很多,所以把串作爲一種獨立的數據結構進行研究。
串(數據結構)
一.串導學
1.課程結構
(概述)
第1章 數據結構、ADT、算法
(基礎)
第2章 線性表
第3章 棧和隊列
第4章 字符串
第5章 數組與廣義表
第6章 樹和二叉樹
第7章 圖
(應用)
第9章 查找
第10章 內部排序
第11章 外部排序
2.知識點搜索
字符串還是一種線性結構,它的存儲也有順序和鏈式兩種結構實現,它的操作和線性表的c操作也是相同的,但是它的這些操作是針對字符串所特有的一些操作,所以我們只是簡單的討論一下串特有的邏輯結構以及操作的實現。
3.串也是一種數據結構
在c/c++中我們也瞭解過字符串了,但是在那裏更像是一種數據類型,而在這我們是當做數據結構。
串是一種特殊的線性表,特殊在:
(1)數據元素都來自字符集!
也就是說串中每一個元素的數據類型都是字符型的,他的數據對象是字符集。
(2)由於數據元素特殊,它的操作有些不同於一般線性表。
例如:操作對象一般是子串(即一組數據元素),而不是單個數據元素
4.串的應用
串的應用是十分廣泛的
比如:搜索引擎中的字符串查找匹配、文字處理軟件wps office、生物信息處理等等,這些實際上都是對字符串進行處理。
5.帶着問題去學習
數據結構中的“串” 和C/C++中的“串” 有何區別?
串與線性表的關係?
“串” 這種數據結構的作用?
掌握:
1.串的特點(與線性表的區別)
2.模式匹配算法(BF,KMP)
二.串的基本概念
1.串的定義
由零個或多個字符組成的有限序列
還要了解幾個定義:
(1)空串:含零個字符的串稱爲空串,用Ф表示。
空串是可以存在的,一定要區分開空串和空格串,空格串是指一個字符串是空格的。
(2)串長:串中所含字符的個數稱爲該串的長度(n)
(2)串的表示:S=“a1a2…an” ,每個ai(1≤i≤n)代表一個字符。
例子:
2.串的操作
操作:(與線性表同的略。因爲字符串是一種特殊的線性結構,所以也有與線性表相同的操作,在此省略)
(1)串相等:當且僅當兩個串的長度相等並且各個對應位置上的字符都相同,稱兩個串相等。
例如,“abc”與“abc”相等,“abc”和“acb”不相等,“abc”和“a bc”不相等
(2)子串:一個串中任意個連續字符組成的子序列(含空串)稱爲該串的子串。
例如,“a”、“ab”、“abc”和“abcd”等都是“abcde”的子串
(3)真子串:是指不包含自身的所有子串。
(注意是 所有 並且不包含自身)
例:“abcde”有多少個真子串?
15個,(注意空串也算是一個子串)
推廣:n(n+1)/2個(利用數學上的等差數列求和即可得出)
(4)空串也是一個子串
3.串的基本操作
串的很多操作都是在子串的基礎上進行操作的
(1)串插入操作
StrInsert(S,pos,T):在串S的位置爲pos的字符之前插入串T
示例: S=“Beig”,
T = “jin”
則執行StrInsert(S,4,T)後
S=“Beijing”,
(2)串刪除操作
StrDelete(S,pos,len):從串S中刪除位置爲pos的字符起長度爲len的子串
示例: S=“Beijing”,
則執行StrDelete(S,4,3)後
S=“Beig”,
(3)串鏈接、求子串、串替換等
4.串與線性表的關係
(1)相同點:
數據元素都來自字符集
操作對象是子串
基本操作:模式匹配
(2)區別:
一般線性表
邏輯結構:一對一
存儲結構:順序表、鏈表
運算規則:隨機、順序存取
操作對象:單個元素
串
邏輯結構:一對一
存儲結構:順序串、鏈式串
運算規則:隨機、順序存取
(串相等、模式匹配)
操作對象:子串
三.模式匹配
(模式匹配是串的重要操作)
1.串的模式匹配
(1)定位:
(串的模式匹配又叫做定位,即在主串T中找是否存在子串P)
①設有主串T和子串P,
②在主串T中找到第一個與子串P相等的子串的位置(第一次出現的位置)
(這個位置當然是子串在主串中出現的第一個字符的位置,然後把這個位置返回)。
(2)目標串:主串 T
(3)模式串:子串 P
(在模式匹配中我們通常把目標串叫做主串,用T表示,同理,模式串爲子串,用P表示)
(4)結果:
①模式匹配成功:在目標串T中找到一個模式串P;
②模式匹配不成功:目標串T中不存在模式串P。
示例:
目標 T : “Beijing”
模式 P : “jin”
匹配結果 = 3
(3呢,就是子串在主串中的位置,當然在這裏是從下標爲0開始計算的,也可以從1開始計算,那就是4了,是比較靈活的)
2.模式匹配的應用
• 自然語言處理:信息檢索
• 生物信息處理:DNA匹配
• 語音識別:輸入的語音符號化
• 網絡安全:病毒入侵檢測
(以上幾塊領域在計算機數據處理中應用很大,可見模式匹配的重要性)
3.模式匹配的作用
(以我們此次的疫情生物病毒舉例)
(1)研究者將人的DNA和病毒DNA均表示成由一些字母組成的字符串序列。
(2)然後檢測某種病毒DNA序列是否在患者的DNA序列中出現過,如果出現過,則此人感染了該病毒,否則沒有感染。
(3)例如:
假設病毒的DNA序列爲baa,
患者1的DNA序列爲aaabbba,則感染,
患者2的DNA序列爲babbba,則未感染。
(注意,人的DNA序列是線性的,而病毒的DNA序列是環狀的)
四.BF算法
1.模式匹配算法
(模式匹配算法我們只學習BF算法和KMP算法,當然還有別的模式匹配算法)
(1)BF算法
①最簡單、最易理解 (我們通常又把它叫做簡單模式匹配算法)
②採用回溯法 (在學習c語言時其實用到過)
(2)KMP算法
(3)BM算法
(4)KR算法
(5)……
2.算法過程
(1)採用回溯法:
①從主串的第i個位置開始,與子串的每個字符逐個比較,若均相等,則找到,位置爲i;
②否則,即在某個位置出現了不等,則說明從該起點開始的子串不是模式串,換一個新起點(第i+1個位置),重新開始,繼續逐一比較,
③直到找到,或i>=Length(T)爲止。
(2)例子:
(在這個例子中起始位置從0開始計算的)
用指針i指向主串T,指針j指向子串P
(3)特點:回溯
每趟的:
①主串回溯到上次起點的下一個位置;
②子串(模式串)回到0
3.算法執行
(1)Index(T,P,pos)
①將主串的第pos個字符和模式的第一個字符比較,若相等,繼續逐個比較後續字符;若不等,從主串的下一字符起,重新與模式的第一個字符比較。
②直到主串的一個連續子串字符序列與模式相等 。返回值爲T中與P匹配的子序列第一個字符的序號,即匹配成功。
③否則,匹配失敗,返回值 0
(2)代碼實現
(這個算法不難,注意我們在這是用的下標從1開始的位置)
int Index(Sstring T,Sstring P,int pos) //T代表主串,p代表子串,pos代表從主串的哪個位置開始找
{
i=pos; //i的初值是pos
j=1; //j的初值是1 ,這裏是從下標爲1開始計算的 ,前面的例子是從0開始計的
while(i<=T.len&&j<=P.len) //即主串沒到頭並且子串沒到頭就循環
{
if(T.data[i]==P.data[j]) //如果兩個串開始有相等的
{
++i; //i++ 主串向後移
++j; //j++ 子串向後移
}
else //否則的話就是不等了 ,就要回溯
{
i=i-j+2; //i-j+2代表i要回溯的那個位置 ,主串回溯到上次起點的下一個位置,如果從0開始計的話,那麼爲i=i-j+1
j=1; //j回溯到1,如果從0開始計的話,j=0;
}
}
if(j>P.len) // 如果j往後走的過程中超過了P子串的長度,即匹配全部成功的話
return i-P.len; //代表i在這一次匹配過程中它最先出發的那個點 下標 位置
else
return 0;
}
(3)爲什麼主串回溯的位置是i-j+2?
4.算法複雜度分析
若n爲主串長度,m爲子串長度
(1)最好情況複雜度:O(m)
目標串的前 m 個字符正好等於模式串的 m 個字符。
例:若n爲主串長度,m爲子串長度
S=‘0001000001’,
T=‘0001’,
pos=1
(2)最壞情況複雜度:O(nm)
每次到最後一個字符才發現不匹配,這時再“倒回去”進行比較
比較次數:m(n-m)(n-m個各比較了m次)
例:若n爲主串長度,m爲子串長度
S=‘0000000001’,
T=‘0001’,
pos=1
(3)算法評價
算法簡單,易於理解,但效率不高。
五.KMP算法
BF算法雖然簡單好理解,但是效率卻不高,接下來的是改進的模式匹配算法,叫做kMP算法。
1.來源
由以下三人同時發明的算法,因此給它起名字爲KMP算法,
D.E.Knuth
J.H.Morris
V.R.Pratt
其中D.E.Knuth(唐納德·克努特)被人們成爲算法和程序設計的先驅者,他有一套經典的著作是計算機程序設計藝術,這套藝術被譽爲算法中真正的聖經。
《計算機程序設計藝術 第1卷 基本算法》
《計算機程序設計藝術 第2卷 半數值算法》
《計算機程序設計藝術 第3卷 排序與查找》
他曾經說過:B-F算法中,匹配失敗後不必完全從頭再來(不必回溯),找到可以利用的信息,跳躍性匹配……
那麼kmp算法就是他所說的改進的模式匹配算法。
2.發現BF算法的改進點
(1)BF算法:
當比較到某個位置不匹配時,不管什麼情況,都進行回溯!回溯到該次比較的起始點的下一個位置,算法效率低。
(2)KMP算法改進:
在匹配失敗時,主串中的指針i不需要回溯,而是在模式中找出適當的字符繼續比較,以此提高效率
利用已經部分匹配這個有效信息,保持i指針不回溯,通過修改j指針,讓模式串儘量地移動到有效的位置。
(已經部分匹配是指子串前面已經有幾個字符匹配好了)
(3)有效:i不回溯,j要從哪裏開始?
通過next函數找到j要移動的位置k
(通過一個自己編寫的函數找到下一次要移動的位置)
3.KMP算法要解決的兩個問題
(1)如何根據失敗函數進行比較?
失敗函數:匹配失敗時,通過next函數求得當前匹配失敗點的位置 j 所對應的移動位置 k
例:假設:失敗函數已經建立
總結KMP算法:
模式 P 的第 j 位失配時:
①若 j > 0,下一趟比較時模式串 P的起始比較位置是P[next(j)],目標串 T 的指針不回溯
(j>0時,j=next(j); i不動)
②若 j = 0,則目標串 T指針進一,模式串P 指針回到 0
(j=0時,j=0; i++)
(2)next函數如何求得?
匹配不成功的那一刻,T[i]!=P[j],但: ‘P0P1…Pj-1’=‘Ti-j+1,Ti-j+2…Ti-1’
假設下一步與模式中第k(k<j)個字符比較,則模式中前k-1個字符必須滿足: ‘P0P1…Pk-1’=‘Pi-k+1,Pi-k+2…Pi-1’
於是得出:
‘P0P1…Pk-1’=‘Pj-k+1,Pj-k+2…Pj-1
4.KMP算法的時間複雜度
(1)設主串T的長度爲 n,模式串P長度爲 m;
(2)求next數組的時間複雜度爲 O(m);
(3)匹配中因主串T不回溯,比較次數可記爲n,
(4)所以KMP算法總的時間複雜度爲 O(n+m)。
5.模式匹配算法比較
(1)BF算法
①最簡單、最易理解
②採用回溯法
(2)KMP算法
①效率較高,可提速到O(n+m)
②主串指針不回溯
6.next函數的改進
例子:
(1)提出問題:
next[j]=k 即 Ti 與 Pj 比較不相等時,繼續與 pk 比較,但是若 pk=pj 顯然肯定還要失敗,所以 next[j] 可以改進。
(2)改進:
next[j] = k,如果Pj=Pk,則 主串中Ti和Pj不等,不需再和pk進行比較,而直接和pnext[k]進行比較。
(3)如何求nextnal:
比較P[j]和P[k],
若不等,則 nextval[j]=k(next[j]);
若相等,nextval[j]=nextval[k];
六.總結
1. 串數據結構的特點:
重點掌握:數據對象限定了字符集,操作對象是一組數據元素
2. 模式匹配算法:BF、KMP
重點掌握:熟悉next[j]函數的定義,會算next[j]和改進的nextval[j]。