問題描述:
將一個字符串a像左旋轉i個位置。例如,當n=8且i=3時(n為字符串有效長度),向量abcdefgh旋轉為defghabc。要求時間復雜度O(n),空間復雜度為O(1)
問題求解:
直接將前i個數組復制到一個臨時數組,將余下的元素左移,再將臨時數組中的i個元素復制到末尾的方法可得正確解,但空間復雜度為O(n)。每次移動一個到末尾,剩余的前移也能得正確解,但時間復雜度為O(n^2)。顯然,我們需要新的想法。
解法一:
移動a[0]到臨時變量t,然后移動a[i]到a[0],a[2i]到x[i],依次類推(將a中所有下標對n取模),直至返回到取x[0]中的元素,此時改為從t取值然后終止過程。
代碼如下:
#include <stdio.h> #include <string.h> //求最大公約數 int gcd(int i,int j) { while(i != j ) { if(i > j) i = i - j; else j = j - i; } return i; } void turnleft(char *a,int i,int n) { int left = i % n; if(left == 0) return ; int gcdnum = gcd(left,n); int j,k,t,m; for(j=0;j<gcdnum;j++) { t = a[j]; m = j; k = m + left; while(1) { k = m + left; if(k >= n) k = k - n; if(k == j) break; a[m] = a[k]; m = k; } a[m] = t; } } int main() { char a[1024]; int i; printf("請輸入字符串: "); //fgets(a,1024,stdin);//fgets會將結尾的回車換行符也記錄下 scanf("%s",a); printf("請輸入左移位數:"); scanf("%d",&i); int n = strlen(a); turnleft(a,i,n); printf("%s\n",a); }
解法二:
解法一滿足了我們的要求,但看起來仍然沒那么直觀,有沒有更清晰移動的方法呢? 答案就是有。
首先將字符串a分為兩段,要移動的前i位為b,剩余部分為c,則字符串可表示為bc,左移i位的結果其實就是cb,則算法的目的就是將bc轉換為cb。首先將b逆序為b',再將c逆序為c',最后將(b'c')'整體逆序即得到cb。
代碼如下:
#include <stdio.h> #include <string.h> //將a中從start到end翻轉 void reverse(char *a,int start, int end) { int i ,j,temp; for(i = start,j = end; i < j; i++,j--) { temp = a[i]; a[i] = a[j]; a[j] = temp; } } //a為原數據,i為要左翼的位數,n為總長度 void turnleft(char *a,int i,int n) { int left = i % n; if(left == 0) return ; reverse(a,0,left-1); reverse(a,left,n-1); reverse(a,0,n-1); return ; } int main() { char a[1024]; int i; printf("請輸入字符串: "); //fgets(a,1024,stdin);//fgets會將結尾的回車換行符也記錄下 scanf("%s",a); printf("請輸入左移位數:"); scanf("%d",&i); int n = strlen(a); turnleft(a,i,n); printf("%s\n",a); }
以上兩種算法均可以在O(n),O(1)內完成字符串的循環移位。也許有看觀會問,這里說的都是循環左移,那循環又移怎么辦呢? 呵,事實上,循環左移和循環右移本質上是一模一樣的,循環右移i位其實就是循環左移n-i位而已。