String Shifting

来源:互联网 发布:微信h5模板源码生成器 编辑:程序博客网 时间:2024/05/21 21:02

题目描述

我们规定对一个字符串的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;}