题意:
给出n,m,以及长度为n的原字符串S[0,n-1]。根据S构造出新的n个字符串A[n],A[i][j]=S[(i+j*m)%n]。求A中最大的串。
tip:
尝试nm之后 可以发现a每n次开始循环(mod后和之前的循环节是同样的几个数,可能不到n),每次由于是取mod 所以也是有循环节的,那么找出这个循环节,找到循环同构中最大的(左移或右移任意位)就可以。。。
最小表示法:
例如,字符串”abcd”的循环同构字符串有:[“abcd”, “bcda”, “cdab”, “dabc”]。
题目的目标是求这些字符串中字典序最小的那个 算法描述 :
令i=0,j=1
如果S[i] > S[j] i=j, j=i+1
如果S[i] < S[j] j++
如果S[i] == S[j] 设指针k,分别从i和j位置向下比较,直到S[i] != S[j]
如果S[i+k] > S[j+k] I+= k+1 ,k = 0,//(j变为标准)
否则 j+=k+1,k = 0 //(i仍是标准)
if(I == j)j++;
返回min(i,j)
类似双支针,一直保留较小的一个。。比较好理解,最大表示除了比较的大于小于变一下就好了
int maxrp(string s){
int Len = s.length(),l = 0,r = 1,k = 0;
while(l < Len && r < Len && k < Len){
int a1 = s[(l+k)%Len] ,a2 = s[(r+k)%Len];
if(a1 == a2) k++;
else if(a1 < a2){
l+= k+1;
k = 0;
}
else {
r += k+1;k = 0;
}
if(l == r) r++;
}
return min(l,r);
}
整个题的代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
string s,ans = "";
const int maxn = 150010;
int len,vis[maxn];
int n,m;
void init(){
scanf("%d%d",&n,&m);
cin >> s;
len = s.length();
}
int maxrp(string s){
int Len = s.length(),l = 0,r = 1,k = 0;
while(l < Len && r < Len && k < Len){
int a1 = s[(l+k)%Len] ,a2 = s[(r+k)%Len];
if(a1 == a2) k++;
else if(a1 < a2){
l+= k+1;
k = 0;
}
else {
r += k+1;k = 0;
}
if(l == r) r++;
}
return min(l,r);
}
void sov(){
int cnt = 0;
for(int i = 0 ; i < len ; i++){
if(vis[i]) continue;
string tmp = "";
cnt++;
int j = i;
while(1){
if(vis[j] == cnt) break;
vis[j] = cnt;
tmp.push_back(s[j]);
j = (j+m)%n;
}
int pos = maxrp(tmp),Len = tmp.length();
tmp += tmp;
string pp = tmp.substr(pos,Len);
if(ans == "" || ans < pp) ans = pp;
}
string nn = "";
int k = ans.length();
int rp = n/k;
for(int i = 1; i <= rp ; i++)
nn += ans;
int mo = n-nn.length();
nn = nn+nn.substr(0,mo);
cout <<nn<<endl;
}
int main(){
//ios::sync_with_stdio(false);
init();
sov();
}