1. 程式人生 > 其它 >字串(String)筆記

字串(String)筆記

技術標籤:c++java資料結構字串

字串(String)

字串相關概念

java:String內建型別,引用資料型別,不可更改,要改的話考慮StringBuffer,StringBuilder,char[]
C++:std::string可更改,也可以用char
C:只有char[]
需要注意的是:
1、C++中"+"運算子複雜度未定義,但通常認為是線性的。
2、在C++中std::string substr和java中的String的subString引數不同。
3、字元範圍:
C/C++[-128,127]通常轉化為undesigned[0,255](跟平臺相關)

java[0,65535](跟平臺相關)

以下記錄幾例關於字串相關的面試題。

例1,0-1交換

把一個0-1串(只包含0和1的串)進行排序,可以交換任意兩個位置,問最少交換的次數?
分析:使用快排partition??最左邊的0和最右邊的1可以不用管
比如000…000001011…111111111

//部分程式碼如下
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,可以利用原來字串的空間

//先刪除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,注意字串要加長
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'; }

這題採用了一種慣用技巧**“倒著複製”**(memery copy),可以保證原始資訊不會被覆蓋。

例3、交換星號

一個字串只包含*號和數字,請把它的星號都放在開頭。
分析:
方法1:快排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]);
}

方法2:採用“倒著複製”,數字相對順序不變

int j=n-1;
for(int i=n-1;i>=0;--i){
	if(isdigit(s[i])) {
		s[j--]=s[i];//如果i是數字,則複製給j;如果i是*號,則跳過
	}
	for(;j>=0;--j){
		s[j]='*';//當i=-1時,說明數字已經複製完了,則將前面的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){//b[i]-'a'就是把b裡面的第i位轉換成0-25中間的一個數
		++nonZero;//nonZero是統計num數組裡面有多少個數字是非0的
	}
}

我們用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;

視窗如何移動?向右移動一位
新視窗a[i-lenb+1, i]
舊視窗a[i-lenb,i-1]
扔掉a[i-lenb],加入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;
}