给定一个字符串,求其循环得到的len个字符串中,字典序最小的那个串的开头是原串的第几个字符
做法:最小表示法(本来想哈希+乱搞的)
什么是最小表示法呢?顾名思义,就是其串循环后字典序最小的那个串来表示整个串。
先上一段代码——应该比网上的精简多了,去掉了一些感觉没什么用的剪枝——可能我错,但是能过。
int work(int n){
int i=0,j=1,k;
while (i<n && j<n){
for (k=0;k<n && s[(i+k)%n] == s[(j+k)%n];k++);
if (s[(i+k)%n] > s[(j+k)%n]) i=i+k+1; else j=j+k+1;
if (i == j) i=j+1;
}
return min(i,j);
}
没错,这就是最小表示法
很短吧。
我们来解析一下具体的做法:首先,考虑用2个指针来扫描,分别从0和1开始。
那么扫描什么呢?得到它们之后最长的相同部分。
假设是s[i]~s[i+k-1]和s[j]~s[j+k-1]——当然,这里不写mod了
首先
s[i]~s[i+k-1]和s[j]~s[j+k-1]这一段是相同的
假设s[i+1]~s[i+k]中的某一个的是最小表示法的开头
设为s[i+t]
那么容易发现,如果s[j+k]<s[i+k],那么说明
s[j+t]比s[i+t]更优,所以s[i+t]不可能成为最小表示法的开头
同理,如果s[j+k]>s[i+k],那么s[j]~s[j+k]都不可能是最小表示法的开头
跑得很快
Executing...
Test 1: TEST OK [0.000 secs, 4276 KB]
Test 2: TEST OK [0.000 secs, 4276 KB]
Test 3: TEST OK [0.000 secs, 4276 KB]
Test 4: TEST OK [0.000 secs, 4276 KB]
Test 5: TEST OK [0.000 secs, 4276 KB]
Test 6: TEST OK [0.000 secs, 4276 KB]
Test 7: TEST OK [0.000 secs, 4276 KB]
Test 8: TEST OK [0.000 secs, 4276 KB]
Test 9: TEST OK [0.000 secs, 4276 KB]
Test 10: TEST OK [0.000 secs, 4276 KB]
Test 11: TEST OK [0.000 secs, 4276 KB]
Test 12: TEST OK [0.000 secs, 4276 KB]
Test 13: TEST OK [0.000 secs, 4276 KB]
Test 14: TEST OK [0.000 secs, 4276 KB]
All tests OK.
据说此题还有后缀数组的做法,而且跑的很慢。
scanf("%*d");
for (int i=0;~scanf("%s",s+i);i+=72);//读入
printf("%d\n",work(strlen(s)));//处理