sgu232(最小表示法)

题意:

给出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();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值