题目:定义rotate(str, i)为把str[i..n-1]和str[0..i-1]拼接而成的字符串,其中n为字符串的长度, 0 =< i < n。
例如,对于字符串str = “abace”
rotate(str, 0) = "abace";
rotate(str, 1) = "bacea";
rotate(str, 2) = "aceab";
rotate(str, 3) = "ceaba";
rotate(str, 4) = "eabac";
现在,将这些字符串排序得到:
abace
aceab
bacea
ceaba
eabac
将这些排序之后的字符串的尾部字符连接起来得到:ebaac,并称之为合法尾部序列lst;将排序之后的第一个字符串abace称为首字符串。现在,已知合法尾部序列lst,如何求首字符串?要求时间复杂度最低。
分析:
(1)如果将首字符连起来得到aabce,并称之为合法首部序列sb,那么sb一定和st排序之后的字符串相等。因此,可以由st得到sb。
a***e
a***b
b***a
c***a
e***c
(2)如果将原来的字符串首尾连接成一个环,那么st[i]一定出现在sb[i]的前面。因此,可以根据一个字符推出紧接其后的下一个字符
a<--e
a<--b
b<--a
c<--a
e<--c
其中"y<--x"表示“x出现的位置恰好在x的前面”关系。
(3)有一种情况需要特别考虑:a在原字符串中出现了多次,如何知道每个a后面的字符呢?例如,对于题目中的例子,可以用如下步骤构造首字符串:首先知道第一个字符为a,然后根据(2)推出的位置关系可以知道b<--a, c<--a,也就是说a后面出现的字符可能为b,也可能为c。那么如何确定到底是哪一个呢?换个角度思考,如果能建立合法首部序列中a和合法尾部序列中a之间的一一对应关系,那么就知道每个a后面的字符。
下面为另一重要性质:
对任一字符x,在排序好的旋转字符串集中,左边为x的旋转字符串按顺序排列如下(其中sk为除掉首字母剩下的字符串):
xs1
xs2
...
xsk
那么有s1<=s2<=..<=sk,据此可知最右端为x的旋转字符串按序排列如下:
s1x
s2x
...
skx
也就是说,左边第一次出现的x与右边第一次出现的x对应;左边第i次出现的x与右边第i次出现的x对应。因此得到了左右字符之间的一一对应关系。
对于题目中的例子,下面来看下如何构造首字符串:首先知道第一个字符为a,此a对应右出现的第一个字符a,因此可得a后面的字符为b;b后面的字符为a;这个a对应第2次出现的a,因此可得紧接着的字符为c;c后面的字符为e。所以得到首字符串为abace。
知道此分析思路,不难写出复杂度为O(n)的算法。(注:对合法尾序列st中的字符进行稳定排序)
代码如下:
#include<stdio.h>
#include<string.h>
#include<malloc.h>
typedef struct _node{
int index;
struct _node * next;
} Node;
typedef struct _head{
int num;
Node* first;
} Head;
typedef struct _index_node{
int index;
char ch;
} Index_node;
int main()
{
char st[1000];
scanf("%s", st);
int n = strlen(st);
Head* heads = (Head*)malloc(sizeof(Head) * 256);//256 buckets, each for one character
memset(heads, 0, sizeof(Head) * n);
Node* nodePool = (Node*)malloc(sizeof(Node) * n);//memory pool
int npos = 0;
int i;
for(i = 0; i < n; i++)//insert each character into the corresponding bucket
{
Node* ptr = nodePool + npos++;
ptr->index = i;
ptr->next = heads[st[i]].first;
heads[st[i]].first = ptr;
heads[st[i]].num++;
}
Index_node* inodes = (Index_node*)malloc(sizeof(Index_node) * n);
int pg = 0;
for(i = 0; i < 256; i++)//unfold the elements in the buckets linearly
{
pg += heads[i].num;
int pl = pg - 1;
while(heads[i].first)
{
inodes[pl].ch = i;
inodes[pl].index = heads[i].first->index;
Node* nptr = heads[i].first;
heads[i].first = heads[i].first->next;
pl--;
}
}
int j;
for(j = 0, i = 0; i < n; i++)//construct the first string
{
printf("%c", inodes[j].ch);
j = inodes[j].index;
}
printf("\n");
free(heads);
free(nodePool);
free(inodes);
return 0;
}