問題:
在《編程珠璣》一書中,提到字符串旋轉問題,比如有字符串ab旋轉得到字符串ba,那麼可以通過(a'b')'得到,其中“ ' ”表示整個字符串旋轉。
在這個問題中,比如"abace"通過在不同位置旋轉,可以得到"eabac","ceaba","aceab","bacea",將這些字符串按照字典排序,得到:
abace
aceab
bacea
ceaba
eabac
這樣得到尾序列爲"ebaac"。問題是給定尾序列"ebaac"如何得到字典排序的首字符串?
分析:
根據尾序列,我們可以得到字符串中所有的字母,然後對字母進行排序,可以得到所有字符串的首字母,由於在旋轉過程中,存在這樣的性質,"a***e"通過右移位,我們可以得到"ea***",因此,字母"e"後面一定是"a",這是一個重要的性質。
a***e
a***b
b***a
c***a
e***c
但是如果存在重複字母呢?比如
b***a
c***a
那麼"a"後面可以是"b"和"c",但是我們根據字典排序知道,"a"開頭的字符串,靠在前面的字符串後面跟的一定是小的字母,所以第一個"a"開頭的字符串後面接的一定是"b",第二個"a"開頭的字符串後面一定接的"c",那麼確定第二個字母,如何確定第三個字母呢?同樣的,確定"b"後,我們根據以"b"結尾的行"a***b",右移一位,得到"ba***",知道"b"後面的字母一定是"a",從而確定b後面是a,然後再根據這個''a"得到後面的"c",由此類推。
代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct A
{
int ind; //記錄下標,這個非常重要
char ch;
};
int comp(const void * x, const void *y)
{
return (((struct A *) x)->ch > ((struct A *) y)->ch
|| ((struct A *) x)->ch == ((struct A *) y)->ch //如果字母相同,下標小的排在前面
&& ((struct A *) x)->ind > ((struct A *) y)->ind);
}
int main()
{
char s[50] = "ebaac", f[50];
int i, j, len;
struct A a[50];
scanf("%s", s);
len = strlen(s);
for (i = 0; i < len; i++)
{
a[i].ind = i;
a[i].ch = s[i];
}
printf("origin:%d:%s\n", len, s);
qsort(a, len, sizeof(struct A), comp); //進行快排
for (i = 0; i < len; i++)
{
printf("%d, %c\n", a[i].ind, a[i].ch);
}
for (i = 0, j = 0; i < len; i++)
{
f[i] = a[j].ch;
j = a[j].ind;
}
f[len] = '\0';
printf("%s\n", f);
return 0;
}
注意:這裏使用下標保存行信息,如下所示:
2 a***e 0
3 a***b 1
1 b***a 2
4 c***a 3
0 e***c 4
總結:
注意分析問題的性質,善於利用性質進行求解。