hdu4333-拓展kmp-Revolving Digits

http://acm.hdu.edu.cn/showproblem.php?pid=4333
拓展kmp,打算在写几道再讲,现在只是理解了大概思想qwq。
给定一个字符串,可以把它后面的字母往前放,问你有多少大于原数,小于原数,等于原数的。qwq
暴力保存了一个,mle。毕竟1e5
后来看了正解,真的不错。原串乘2 是这种情况
这里写图片描述
模式串(len) 和 目标串的子串(长度也为len)比较,如图 上面。
打个比方 123 和 124 前面相等,只比较后面的。相当于先求他和目标串的前缀长度x,然后一个一个是 x+1,另一个是x+i+1.(在母串中)。
还有一个 代码里 查找 模式串循环节那个,在图中下面那个,大概就是 模式串长度能整除 黑色卡着的部分。那么就能算出来qwq。。好神奇

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
/* 拓展kmp是对kmp的一种拓展,
用来求  目标串 和模式串的 后缀 的最长公共前缀。
我们发现,当最长公共前缀为目标串的长度时,其实就是判断
模式串是否在模式串中出现的次数。
最长公共前缀标定 extends[]数组,
如果采用暴力的计算方法,是不可取的。
这里面 利用了 前缀之间的性质。

本题还有一个厉害的地方,那就是计算 一个字符串的循环节。
*/
const int maxn=2e5+100;
int nex[maxn];
int extand[maxn];
int getnext(int len,char s[]){
    nex[0]=-1;
    int i=0;int k=-1;
    while(i<len){
          if(k==-1||s[i]==s[k]){
             nex[++i]=++k;
          }
          else
             k=nex[k];
    }
   // for(int i=0;i<=len;i++)
    //cout<<nex[i]<<endl;
}
void getnext(char T[]){
     int len=strlen(T),a=0;
     extand[0]=len;
     while(a<len-1&&T[a]==T[a+1]) a++;
     extand[1]=a;
     a=1;
     for(int k=2;k<len;k++){
         int p=a+extand[a]-1,L=extand[k-a];
         if((k-1)+L>=p){
             int j=(p-k+1)>0? (p-k+1):0;
             while(k+j<len&&T[k+j]==T[j]) j++;
             extand[k]=j;
             a=k;
         }
         else extand[k]=L;
     }
}
int main(){
    ios::sync_with_stdio(false);
    char s[maxn];
    int t;
    cin>>t;
    for(int tt=1;tt<=t;tt++){
        cin>>s;
        int len=strlen(s);
        getnext(len,s);
        for(int i=0;i<len;i++){
           s[i+len]=s[i];
        } s[len*2]='\0';
        //cout<<s<<endl;
        int siz=len-nex[len];
        int k;
        //cout<<nex[len]<<endl;
        if(!(len%siz)){
            k=len/siz;
        }
        else
        k=1;
        getnext(s);
        int l=0,r=0,mid=0;
        for(int i=0;i<len;i++){
            if(extand[i]>=len) mid++;
            else if(s[extand[i]]>s[extand[i]+i]) l++;
            else if(s[extand[i]]<s[extand[i]+i]) r++;
        }
       cout<<"Case "<<tt<<": "<<l/k<<" "<<mid/k<<" "<<r/k<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值