问题:
在《编程珠玑》一书中,提到字符串旋转问题,比如有字符串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
总结:
注意分析问题的性质,善于利用性质进行求解。