String Shifting

题目描述

我们规定对一个字符串的shift操作如下:
shift(“ABCD”, 0) = “ABCD”
shift(“ABCD”, 1) = “BCDA”
shift(“ABCD”, 2) = “CDAB”
换言之, 我们把最左侧的N个字符剪切下来, 按序附加到了右侧。
给定一个长度为n的字符串,我们规定最多可以进行n次向左的循环shift操作。如果shift(string, x) = string (0<= x <n), 我们称其为一次匹配(match)。求在shift过程中出现匹配的次数。

输入
输入仅包括一个给定的字符串,只包含大小写字符。
样例输入
byebyebye
输出
输出仅包括一行,即所求的匹配次数。
样例输出
3

开始我想用暴力求解,时间复杂度为O((n-m+1)*m),超时了

#include<iostream>
#include<string>
using namespace std;

string shiftn(string s, int n)
{
      string temp = s+s;
      return temp.substr(n,s.size());
}

int main()
{
     string s;
     cin>>s;
     int count=0;
     for(int i =0;i < s.size();i++)
     {
          if(s ==shiftn(s, i))
                count++;
     }
     cout<<count<<endl;
     return 0;
}*

然后用字符串匹配的Rabin-Karp 算法
这个算法可以先从只有数字(0-9)的字符串理解,对于一个长度为m的【匹配字符串】P[1…m],用p表示该字符串对应的含有m个数字的整形数,我们用Ts来表示【被匹配字符串】T[s+1, … , s+m] 这m个字符对应的整形数值,不难发现,当两个数值相等时,这两个数值对应的字符串就相等,也就是当且仅当p = Ts有 P[1…m] = T[s+1,…,s+m]. 把数字从字符串转换为对应的整形数值,时间复杂度为O(m).通过下面的公式进行转换即可:
p = P[m] + 10(P[m-1] + 10(P[m-2]+ …. + (10(P[2]) + P[1])).

RK算法有一个巧妙之处在于如何通过Ts去计算Ts+1,且时间为O(1)
Ts+1 =10*(Ts - 10^m−1 * T[s+1]) + T[s+m+1]
但是这里的Ts有可能太大而溢出,通常在后面mod q,q是一个大奇数,但这样又出现一个问题,这里p=ts时就不代表两个串匹配了,需要暴力比较一次才可以确定
利用这个思想,可以O(1)时间模拟出这个字符串的变化再去比较,这样就不会超时了,代码如下:

#include<iostream>
#include<string>
#include<cmath>
using namespace std;

int key = 3;//进制基数

int change(char c)//大写字母0~25,小写字母26~51
{
    if(c >='A' && c<='Z')
        return c-'A';
    return 26+c-'a';
}

int main()
{
     string s;
     cin>>s;
     int count=1,i;
     long long  pre = 0, cur = 0, num = 1;
     for(i =0;i < s.size();i++)
     {
         pre = pre*key + change(s[i]);//计算匹配字符串对应的整数值
         num = num*key;//num是s对应位的最大值pow(key, s.size())
     }
     cur = pre;
     for(i =0; i < s.size()-1;i++)
     {
         int temp = change(s[i]);
         cur = cur*key - temp*num + temp;//计算下一个被匹配字符串的整数值
         if(cur == pre)
             count++;
     }
     cout<<count<<endl;
     return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值