1. 字串是什麼
字串(string) 是由 n 個字元組成的乙個有序整體( n >= 0 )。例如,s = 「beijing」 ,s 代表這個串的串名,beijing 是串的值。這裡的雙引號不是串的值,作用只是為了將串和其他結構區分開。字串的邏輯結構和線性表很相似,不同之處在於字串針對的是字符集,也就是字串中的元素都是字元,線性表則沒有這些限制。
在實際操作中,我們經常會用到一些特殊的字串:
s ="";
// 表示空串,指含有零個字元的串,書面中也可以直接用 ø 表示。
s =" "
;//不是空串,表示空格串,只包含空格的串。空格串中是有內容的,只不過內容是空格,且空格串中可以包含多個空格。
子串:串中任意連續字元組成的字串叫作該串的子串。
原串通常也稱為主串。例如:
a =
"bei"
;b =
"beijing"
;c =
"bjingei"
;
對於字串 a 和 b 來說,由於 b 中含有字串 a ,所以可以稱 a 是 b 的子串,b 是 a 的主串;
而對於 c 和 a 而言,雖然 c 中也含有 a 的全部字元,但不是連續的 「bei」 ,所以串 c 和 a 沒有任何關係。
當要判斷兩個串是否相等的時候,就需要定義相等的標準了。只有兩個串的串值完全相同,這兩個串才相等。根據這個定義可見,即使兩個字串包含的字元完全一致,它們也不一定是相等的。例如b = "beijing"
,c = "bjingei"
,則 b 和 c 並不相等。
字串的儲存結構與線性表相同,也有順序儲存和鏈式儲存兩種。
在鏈式儲存中,每個結點設定字元數量的多少,與串的長度、可以占用的儲存空間以及程式實現的功能相關。
因此,串的鏈式儲存結構除了在連線串與串操作時有一定的方便之外,總的來說,不如順序儲存靈活,在效能方面也不如順序儲存結構好。
2. 字串的基本操作
2.1 字串的新增操作
字串的新增操作和陣列非常相似,都牽涉對插入字串之後字元的挪移操作,所以時間複雜度是 o(n)。
例如,在字串s1 = "123456"
的正中間插入s2 = "abc"
,則需要讓 s1 中的 「456」 向後挪移 3 個字元的位置,再讓 s2 的 「abc」 插入進來。很顯然,挪移的操作時間複雜度是 o(n)。不過,對於特殊的插入操作時間複雜度也可以降低為 o(1)。這就是在 s1 的最後插入 s2,也叫作字串的連線,最終得到 「123456abc」。
2.2 字串的刪除操作
字串的刪除操作和陣列同樣非常相似,也可能會牽涉刪除字串後字元的挪移操作,所以時間複雜度是 o(n)。
例如,在字串s1 = "123456"
的正中間刪除兩個字元"34"
,則需要刪除 「34」 並讓 s1 中的 「56」 向前挪移 2 個字元的位置。很顯然,挪移的操作時間複雜度是 o(n)。不過,對於特殊的插入操作時間複雜度也可以降低為 o(1)。這就是在 s1 的最後刪除若干個字元,不牽涉任何字元的挪移。
2.3 字串的查詢操作
字串的查詢操作,是反映工程師對字串理解深度的高頻考點,這裡需要你格外注意。
例如,字串s = "goodgoogle"
,判斷字串t = "google"
在 s 中是否存在。需要注意的是,如果字串 t 的每個字元都在 s **現過,這並不能證明字串 t 在 s **現了。當 t = 「dog」 時,那麼字元 「d」、「o」、「g」 都在 s **現過,但他們並不連在一起。
那麼我們如何判斷乙個子串是否在字串**現過呢?這個問題也被稱作子串查詢或字串匹配,接下來我們來重點分析。
子串查詢(字串匹配):
首先,我們來定義兩個概念,主串和模式串。我們在字串 a 中查詢字串 b,則 a 就是主串,b 就是模式串。我們把主串的長度記為 n,模式串長度記為 m。由於是在主串中查詢模式串,因此,主串的長度肯定比模式串長,n>m。因此,字串匹配演算法的時間複雜度就是 n 和 m 的函式。
假設:
要從主串 s = 「goodgoogle」 中找到 t = 「google」 子串。根據我們的思考邏輯,則有:
首先,我們從主串 s 第 1 位開始,判斷 s 的第 1 個字元是否與 t 的第 1 個字元相等。
如果不相等,則繼續判斷主串的第 2 個字元是否與 t 的第1 個字元相等。直到在 s 中找到與 t
第乙個字元相等的字元時,然後開始判斷它之後的字元是否仍然與 t 的後續字元相等。
如果持續相等直到 t 的最後乙個字元,則匹配成功。
如果發現乙個不等的字元,則重新回到前面的步驟中,查詢 s 中是否有字元與 t 的第乙個字元相等。
如下圖所示,s 的第1 個字元和 t 的第 1 個字元相等,則開始匹配後續。直到發現前三個字母都匹配成功,但 s 的第 4個字母匹配失敗,則回到主串繼續尋找和 t 的第乙個字元相等的字元。
如下圖所示,這時我們發現主串 s 第 5 位開始相等,並且隨後的 6 個字母全匹配成功,則找到結果。
這種匹配演算法需要從主串中找到跟模式串的第 1 個字元相等的位置,然後再去匹配後續字元是否與模式串相等。顯然,從實現的角度來看,需要兩層的迴圈。第一層迴圈,去查詢第乙個字元相等的位置,第二層迴圈基於此去匹配後續字元是否相等。因此,這種匹配演算法的時間複雜度為 o(nm)。其**如下:
public
voids1(
) jc = j;}if
(jc == t.
length()
-1)}
} system.out.
println
(isfind)
;}
3. 字串匹配演算法的案例
最後我們給出一道面試中常見的高頻題目,這也是對字串匹配演算法進行拓展,從而衍生出的問題,即查詢出兩個字串的最大公共字串。
假設:
有且僅有 1 個最大公共子串。比如,輸入 a = 「13452439」, b = 「123456」。由於字串 「345」 同時在 a 和 b **現,且是同時出現在 a 和 b 中的最長子串。因此輸出 "345」。
對於這個問題其實可以用動態規劃的方法來解決,關於動態規劃,我們會在後續的課程會講到,所以在這裡我們沿用前面的匹配演算法。
假設字串 a 的長度為 n,字串 b 的長度為 m,可見時間複雜度是 n 和 m 的函式。
首先,你需要對於字串 a 和 b 找到第乙個共同出現的字元,這跟前面講到的匹配演算法在主串中查詢第乙個模式串字元一樣。
然後,一旦找到了第乙個匹配的字元之後,就可以同時在 a 和 b 中繼續匹配它後續的字元是否相等。這樣 a 和 b
中每個互相匹配的字串都會被訪問一遍。全域性還要維護乙個最長子串及其長度的變數,就可以完成了。
從**結構來看,第一步需要兩層的迴圈去查詢共同出現的字元,這就是 o(nm)。一旦找到了共同出現的字元之後,還需要再繼續查詢共同出現的字串,這也就是又巢狀了一層迴圈。可見最終的時間複雜度是 o(nmm),即 o(nm²)。**如下:
public
voids2(
)if(max_len < m-i+1)
}}}}
system.out.
println
(maxsubstr)
;}
4. 總結
字串的邏輯結構和線性表極為相似,區別僅在於串的資料物件約束為字符集。但是,字串的基本操作和線性表有很大差別:
在字串的基本操作中,通常以「串的整體」作為操作物件;
字串的增刪操作和陣列很像,複雜度也與之一樣。但字串的查詢操作就複雜多了,它是參加面試、筆試常常被考察的內容。
面試經典 字串匹配問題
字串匹配問題描述 設有兩個字串a和b,a字串長度為n,b字串長度為m,現在要確定b字串是否出現在a字串中,如果是,返回位置,如果不是返回結果為false,在這裡,我們成字串b為模式字串,a為主串。最近經常會被問到字串匹配的問題,實際上這些問題在網上都有很多資源,我覺得沒有必要自己寫乙個,但是看了之後...
字串高頻面試題。
一 題目 把乙個01 只包含0和1的串 串進行排序。可以交換任意兩個位置,求最少交換的次數。方法 仿造快速排序裡面的partition的過程。最左邊的0和1是沒有意義的,從左到右掃到第乙個1,從右到左掃到第乙個0,然後交換,然後繼續掃下去,就ok啦。include include define ma...
字串匹配問題
在字串匹配問題中,我們期待察看串t中是否含有串p。其中串t被稱為目標串,串s被稱為模式串。進行字串匹配,最簡單的乙個想法是 public class match 可以看見,這個演算法 假定m n 的複雜度是o mn 其中m是t的長度,n是p的長度。這種演算法的缺陷是匹配過程中帶有回溯 準確地說是t串...