1. 程式人生 > >字串面試題精講

字串面試題精講

字串String

字串簡介 面試題總體分析 一些例題 例1  0-1串交換排序 例2  字元的替換和複製 例3  交換星號 例4  子串變位詞 例5  單詞(字串)翻轉 總結

-通常作為字元陣列。

-Java:string內建型別,不可更改,要更改的話可考慮轉StringBuffer,StringBuilder,char[]之類

-C++:std:string可更改,也可以考慮用char[](char *)

-C只有char[]

注意

(1)C++中“+”運算子,複雜度未定義,通常認為是線性的。

(2)C++ std::stringsubstr和String的subString引數不同

(3)C/C++字元範圍是[-128,127],轉換為unsigned[0,+255];Java中[0,65535].

10-1交換

把一個0-1串(只包含0和1的串)進行排序,你可以交換任意兩個位置,問最少交換的次數?(國內某公司最新線上筆試題)

分析:快排partition?最左邊的那些0和最右邊的那些1都可以不管

00…0001…….0111….1

int answer = 0;      

for (int i = 0, j= len – 1; i < j; ++i, --j) {

       for (;(i < j) && (a[i] ==‘0’);++i);

       for (;(j > i) && (a[j] ==‘1’); --j);

       if (i < j) ++answer;

}

2 字元替換和複製

刪除一個字串所有的a,並且複製所有的b。注:字元陣列足夠大

分析:先刪除a,可以利用原來字串的空間

int n = 0, numb =0;

for (int i = 0;s[i]; ++i) {

       if (s[i] != ‘a’) { s[n++] = s[i];}

       if (s[i] == ‘b’) { ++numb;}

}

s[n] = 0;

再複製b,注意字串要加長

先計算字串裡有幾個b,得到複製後的長度

然後“倒著”複製——慣用技巧

int newLength = n+ numb;

s[newLength] = 0;

for (int i =newLength - 1, j = n – 1; j >=0; --j) {

       s[i--] = s[j];

       if (s[j] == ‘b’) s[i--] = ‘b’;

}

思考題:如何把字串的空格變成”%20”? 同樣,字元陣列足夠大!

3 一個字串只包含*和數字,請把它的*號都放開頭。

方法快排partition——數字相對順序會變化

迴圈不變式: [0..i – 1]都是*, [i..j – 1]是數字,[j..n– 1]未探測

for (int i = 0, j= 0; j < n; ++j)

              if (s[j] == ‘*’) swap(s[i++], s[j]);

樣例 *01*2*4

 i=0,  j= 0, *01*2*4  交換s[0],不變,i = 1

 i=1,  j= 1, *01*2*4  不變

 i = 1,j = 2, *01*2*4 不變

 i=1, j = 3, 交換s[1],s[3]變為 **102*4 並且i = 2

 i=2, j = 4,**102*4不變

 i = 2,j = 5,  交換s[2],s[5]變為***0214 且i = 3

 再往後沒變化了

方法2數字相對順序不變

倒著

int j = n – 1;

       for (int i = n – 1; i >= 0; --i)

                     if (isdigit(s[i])) s[j--] =s[i];

       for (; j >= 0; --j) s[j] = ‘*’;

4 子串變位詞

給定兩個串a和b,問b是否是a的子串的變位詞。例如輸入a = hello, b = lel, lle, ello都是true,但是b = elo是false。(國外某公司最新面試題)

滑動視窗的思想

動態維護一個“視窗”。

比如b的長度是3,我們考察a[0..2], [1..3],[2..4]是否和b是變位詞

如何與b比較?

我們用一個hash,基於字串的特殊性,我們可以用[0..255]或者[0..65535]的陣列,我們暫且認為它們都是小寫英文字母,用[0..25]來表示b中每個單詞出現多少次。

我們可以存一下有多少個非0次出現的,以後有用

       int nonZero = 0;

       for (int i = 0; i < lenb; ++i)

              if (++num[b[i] – ‘a’] == 1)++nonZero;

我們用b中的次數減去a中一個“視窗”內的字元種類,如果結果全是0,則找到這樣的子串了。 注意num[]的含義變為了字元種類差

第一個視窗 [0..lenb – 1] (注意lena < lenb無解)

       for (int i = 0;  i < lenb; ++i) {

              int c = a[i] – ‘a’;

              --num[c];

              if (num[c] == 0) --nonZero;

              else if (num[c] == -1) ++nonZero;

       }

       if (nonZero == 0) return true;

o視窗如何滑動?向右移動一位

n 新視窗a[i - lenb + 1..i]

n 舊視窗a[i – lenb.. i – 1]

o 扔掉a[i – lenb]

o 加入a[i]

for (int i = lenb;i < lena; ++i) {

              int c = a[i – lenb] – ‘a’;

              ++num[c];

              if (num[c] == 1) ++nonZero;

              else if (num[c] == 0) --nonZero;

              c = a[i] – ‘a’;

              --num[c];

              if (num[c] == 0) --nonZero;

              else if (num[c] == -1) ++nonZero;

              if (nonZero == 0) return true;

}

5、單詞翻轉:翻轉句子中全部的單詞,單詞內容不變

例如I’m a student.  變為student. a I’m

in-place翻轉 字串第i位到第j位

while (i < j)swap(s[i++], s[j--]);

有什麼用?

翻轉整個句子 : .tneduts a m’I

每個單詞單獨翻轉: student. a I’m

難點? 如何區分單詞?找空格,split

思考題: 字串迴圈移位abcd

移動1次變為bcda

移動2次變為cdab

移動3次變為dabc

結論: 長度為n, 移動m次,相當於移動m % n次

前m % n位翻轉, 後n – m % n位翻轉

總體再翻轉一次 試驗一下?

總結:

我理解的in-place (原地)

-本身O(1)空間

-遞迴,堆疊空間可以不考慮

原地相關的問題

- 字串迴圈左移、右移動

-快排partition相關

滑動視窗

- 能達到O(n)的的時間複雜度

-O(1)的空間複雜度

規則相關——細緻

匹配 (暴力):KMP比較少見

Manacher——要求比較高的筆試