标签:
给你一个字符串, 然后把他想象成一个环。 从某一个地方断开,然后逆时针或顺时针, 都可以形成一个字符串, 求字典序最大的那种。 输出断开位置以及是顺时针还是逆时针。
如果两个一样, 输出位置靠前的一个, 如果位置也一样, 输出顺时针的那个。
显然是一个最大表示法。 麻烦的是逆时针的情况, 因为要输出位置靠前的一个。 但是逆时针求出来的是字符串反转之后靠前的, 所以应该求一遍kmp, 找出来最靠后的一个位置。
#include <iostream> #include <cstdio> #include <algorithm> #include <string> using namespace std; const int maxn = 4e4+5; int a[maxn], b[maxn], pos[maxn], nxt[maxn], c[maxn]; int getMax(int a[], int n) { int i, j, k; for(i = 0, j = 1; i<n&&j<n; ) { for(k = 0; k<n&&a[i+k]==a[j+k]; k++) ; if(k>=n) break; if(a[i+k]>a[j+k]) j += k+1; else i += k+1; if(i == j) j++; } return i; } int solve(string s, int a[], int n) { for(int i = 0; i < n; i++) { a[i] = s[i]-‘a‘; } for(int i = n; i < 2*n-1; i++) { a[i] = a[i-n]; } int pos = getMax(a, n); return pos; } void init_kmp(int m) { int i = 0, j = -1; nxt[0] = -1; while(i<m) { if(j == -1 || c[i] == c[j]) { ++i, ++j; nxt[i] = j; } else { j = nxt[j]; } } } int kmp(int n) { int cnt = 0; int i = 0, j = 0; while(i<2*n-1) { if(j == -1 || b[i] == c[j]) { i++, j++; } else { j = nxt[j]; } if(j == n) { pos[cnt++] = i; j = nxt[j]; } } return cnt; } int main() { int t, n; string s; cin>>t; while(t--) { cin>>n>>s; int pos1 = solve(s, a, n); int num = 0, flag = -1; reverse(s.begin(), s.end()); int pos2 = solve(s, b, n); int tmp1 = pos1, tmp2 = pos2; while(num < n) { if(a[pos1] > b[pos2]) { flag = 1; break; } else if(a[pos1] < b[pos2]) { flag = 0; break; } else { pos1++; pos2++; num++; } } int cnt = 0; for(int i = tmp2; i < tmp2+n; i++) { c[cnt++] = b[i]; } init_kmp(n); cnt = kmp(n); tmp2 = pos[cnt-1]%n; if(num == n) { if(tmp1+1 <= n-tmp2) { flag = 1; } else { flag = 0; } } if(flag) { printf("%d %d\n", tmp1+1, !flag); } else { printf("%d %d\n", n-tmp2, !flag); } } return 0; }
hdu 5442 Favorite Donut 最大表示法+kmp
标签:
原文地址:http://www.cnblogs.com/yohaha/p/5722826.html