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