題目描述:
定義字串的左旋轉操作:把字串前面的若干個字元移動到字串的尾部。
如把字串abcdef左旋轉2位得到字串cdefab。
請實現字串左旋轉的函式,要求對長度為n的字串操作的時間複雜度為o(n),空間複雜度為o(1)。
首先第一種最直觀的解法:把陣列字元乙個個的左移:
class leftrotatestring\r\nresult:
", str, result);
console.read();
}public
string method1a(string str, int k)
chararray[chararray.length - 1] = firstchar;
}return
newstring(chararray);
}}
注意,string型別是不可變的,如果你用str[0] = str[1],不能編譯通過。
雖然功能可以實現,但是演算法複雜度為o(k * n),不符合要求。繼續探索。
大家開始可能會有這樣的潛在假設,k
尤其在程式設計的時候,全面地考慮問題是很重要的,k可能是乙個遠大於n的整數,在這個時候,上面的解法是需要改進的。
仔細觀察迴圈右移的特點,不難發現:每個元素右移n位後都會回到自己的位置上。因此,如果k > n,右移k-n之後的陣列序列跟右移k位的結果是一樣的。
進而可得出一條通用的規律:
右移k位之後的情形,跟右移k』= k % n位之後的情形一樣
publicstring method1b(string str, int k)
chararray[chararray.length - 1] = firstchar;
}return
newstring(chararray);
}
這只是**技巧~~演算法複雜度降為o(n^2),達不到o(n)的要求。
三次翻轉法:
假設原陣列序列為abcd1234,要求變換成的陣列序列為1234abcd,即迴圈右移了4位。比較之後,不難看出,其中有兩段的順序是不變的:1234和abcd,可把這兩段看成兩個整體。左移k位的過程就是把陣列的兩部分交換一下。
變換的過程通過以下步驟完成:
逆序排列abcd:abcd1234 → dcba1234;
逆序排列1234:dcba1234 → dcba4321;
全部逆序:dcba4321 → 1234abcd。
publicvoid reverse(char chararray, int begin, int end)
}public
string leftshift(string str, int k)
ok, 到這裡為止,總算滿足條件了,演算法複雜度為o(2n) = o(n)。
我在想,為什麼能想出這樣好的點子呢?這裡用到了線性代數矩陣的知識。哈哈,這裡體現到數學的力量了!
我們還是把字串看成有兩段組成的,記位xy。左旋轉相當於要把字串xy變成yx。我們先在字串上定義一種翻轉的操作,就是翻轉字串中字元的先後順序。把x翻轉後記為xt。顯然有(xt)t=x。
我們首先對x和y兩段分別進行翻轉操作,這樣就能得到xtyt。接著再對xtyt進行翻轉操作,得到(xtyt)t=(yt)t(xt)t=yx。
那麼,到此結束了麼?no,要是到此結束,那就失去樂趣了。
題目是限制空間複雜度o(1),那麼如果不限制呢?哈哈,那就太簡單了。
publicstring method2(string str, int k)
for (int i = str.length - k, j = 0; i < str.length; i++, j++)
return
newstring(chararray);
}
時間複雜度就是o(n),但是空間複雜度是o(k)了。可惜了,不符合題目要求啊,不過這樣以空間換時間的做法還是挺好的。
難道就沒有更好的方法了麼?難道就沒有強大的數學公式解法之類的?
好吧,讓我們反向從結果看看有木有什麼規律。
對於"abcdefg" 左移3來說,你看結果,會發現這樣神奇的規律:
publicstring leftshift2(string str, int k)
通過這一連串的賦值,就可以用o(n)的時間複雜度和o(1)的空間複雜度來解決。但是這是針對"abcdefg"左移3實現的。
我們需要擴充套件到一般應用。其實從結果我們可以看出來了,這一系列的賦值關鍵是index的計算。
聰明同學應該發現了規律——index是乙個迴圈鏈,當index>str.length時,指標又從左開始(取模運算),前乙個end 就變成現在的start。
於是我很快的寫出了這個程式:
//////此**有bug
///public
string leftshiftwrong(string str, int k)
chararray[end] = temp;
return
newstring(chararray);
}
我很高興,它的結果
對於"abcdefg" 左移3來說是正確的。
但是沒過多久,我在午休小憩的時候,靈光一閃,這個程式有個嚴重的bug,對於"abcdef"左移2來說,結果是錯的!(不知道你有沒有靈光一閃呢?我發現自己在程式設計的時候總是沒有,但是當我休息的時候卻會有靈光一閃。看來還得多休息啊!!!)
原因在於對於
"abcdefg" 左移3來說,3和7是互為質數的,除不盡,因此整個迴圈鏈一直串起來。
但是對於
"abcdef"左移2來說,2和6不是互為質數的,有最大公約數2,迴圈鏈會在0的時候斷掉。需要繼續下一條迴圈鏈。
可以看看
"abcdef"左移2的手工解法:
publicstring leftshift2(string str, int k)
從中可以看出,這種情況,應該有多條子迴圈鏈。條數 = k 和 str.length的 最大公約數。
於是,終於可以寫成正確並且高效的程式了:
//////左移字串。時間複雜度o(n), 空間複雜度o(1)
///public
string leftshiftcorrect(string str, int k)
else
}return
newstring(chararray);
}
完了麼?沒完。接下來我擴充套件成通用的陣列迴圈移位:
//////陣列迴圈移位。時間複雜度o(n), 空間複雜度o(1)
///k>0為左移,k
<0為右移
///public
void shift(t array, int k)
else
}}
哎,你說了,這玩意有嘛用啊?花了這兩天搞這玩意,有意思麼?
」愛你一萬年」--剪下成「一萬年愛你」,就可以用上面陣列的迴圈移位演算法實現。
其實從結果分析,最終的字串"
愛你一萬年"內容沒變,只是輸出順序變了,而我們上面的演算法呢,不管怎麼做,都是要移動字串。時間複雜度最少為o(n)。那麼能不能不移動字串內容呢,用乙個強大的公式,來輸出新的順序呢?
有時候,從純演算法角度走到頭了,我們必須換個思路,以資料結構的方式來解決問題。
於是,我就構建了下面這個資料結構,來實現旋轉後順序的輸出。公式很簡單,就是自己計算什麼時候是第乙個要輸出的index。
class rotatestructurepublic
void shift(int k)
public t this[int index]
}public ienumerableoutput()
}
於是,這樣就實現了時間複雜度o(1),空間複雜度o(1)的演算法呢。
呵呵,差不多暫時到這邊了。
那個,誰有好工作推薦啊?求南京地區的 .net開發,要求就一條,技術含量高的,團隊氣氛活躍的,有發展前景的啊!
C 左旋轉字串
問題描述 定義字串的左旋轉操作 把字串前面的若干個字元移動到字串的尾部,如把字串 abcd 左旋轉 2 位得到字串 cdab 請實現字串左旋轉的函式。演算法分析 1.暴力移位法 a 把最後乙個字元取出並儲存起來,然後z最後乙個字元前面的所有字元後移,b 儲存的最後乙個字元存放到第乙個字元的位置,c ...
左旋轉字串
題目 定義字串的左旋轉操作 把字串前面的若干個字元移動到字串的尾部。如把字串abcdef左旋轉2位得到字串cdefab。請實現字串左旋轉的函式。要求時間對長度為n的字串操作的複雜度為o n 輔助記憶體為o 1 思想 旋轉三次 include include using namespace std v...
左旋轉字串
package com.string 旋轉字串 q 26 左旋轉字串 題目 定義字串的左旋轉操作 把字串前面的若干個字元移動到字串的尾部。如把字串abcdef左旋轉2位得到字串cdefab。請實現字串左旋轉的函式。要求時間對長度為n的字串操作的複雜度為o n 輔助記憶體為o 1 public cla...