HDU 4333 Revolving Digits
- 题意:给一个长度为10^100000的数,让我们求它循环移位可以得到的不同数字中(去重去重去重!!!),小于它的个数、等于它的个数、大于它的个数。
思路:
数据范围过大,肯定是读字符串啦~但是怎么处理呢?既然是循环移位得到的数字,那么我们将字符串完整复制放在末尾,就可以得到所有移位可以得到的数字。举个栗子:t = 123,s = 123123
扩展KMP
那么我们以 s 为文本串,以 t 为模式串跑扩展KMP,得到 s 的extend[ ]. 那么就有以下三种情况
- 当extend[ i ] == t_len时,说明s[i, i + 1, ..., i + t_len)和模式串 t 相等
- 当s[ i + extend[ i ] ] > t[ extend[ i ] ]时,说明s[i, i + 1, ..., i + t_len)大于模式串 t. 【extend[ i ]是s[i, ... s_len)与 t 的最大相同前缀的长度,所以两者第一个不同的字符就是s[ i + extend[ i ] ]和t[ extend[ i ] ] 】
- 当s[ i + extend[ i ] ] < t[ extend[ i ] ]时,说明s[i, i + 1, ..., i + t_len)小于模式串 t. 【原理与上面第二条类似】
去重(KMP)
接下来就到了去重的时候了!我们需要判断原串是不是有循环节!比如这种101010,那么10就是它的循环节. 因为题目要求不能有重复数字的嘛,所以相等的肯定就是1个不可置疑!那么如果相等的个数有kind个,那么就说明三个答案都重复了kind次。【所以说到这里,重复次数就是_equal(和原串相等的个数)??于是发现根本用不到KMP,直接扩展KMP,然后遍历一遍就完事,每个答案都除以_equal即可,代码贴了一份放最后了~】
但还是说完KMP叭TAT!举个栗子:t = 1010,s = 10101010
因为t = 1010 有两个循环节10. 所以会重复两次。
- 我们知道循环节的长度可以由cycL = len - Next[ len ]得到。如果不知道为什么的建议做做这道题。
- 重复次数=循环节的个数= len / cycL。
- 如何判断字符串是不是循环节个数>1的循环字符串?Next[len] != 0 && len % cycL == 0
END
OS: 又双叒叕是怀疑大佬博客的一天!TAT,因为读错题,导致我带着我以为的题意去看他们的博客,于是我GG了!TAT. 怎么就是没看到different!!!!!!!!!!!!!TAT...
不过又复习了下KMP的next[]循环节问题,还是颇有收获滴~
AC CODE【扩展KMP+(其实可以废掉的)KMP做法】
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 2000000 + 7;
int Next[maxN], extend[maxN];
void GetNext(string t, int len)
{
int front = 0, p = 0;
Next[0] = len;
for(int i = 1; i < len; i ++ )
{
if(i >= p || i + Next[i - front] >= p)
{
if(i >= p) p = i;
while(p < len && t[p] == t[p - i]) ++ p;
Next[i] = p - i;
front = i;
}else Next[i] = Next[i - front];
}
}
void GetExtend(string s, int s_len, string t, int t_len)
{
GetNext(t, t_len);
int front = 0, p = 0;
for(int i = 0; i < s_len; i ++ )
{
if(i >= p || i + Next[i - front] >= p)
{
if(i >= p) p = i;
while(p < s_len && p - i < t_len && s[p] == t[p - i]) ++ p;
extend[i] = p - i;
front = i;
}else extend[i] = Next[i - front];
}
}
void kmp(string t, int len)
{
int i = 0, j = -1;
Next[0] = -1;
while(i < len)
{
if( j == -1 || t[i] == t[j])
{
++ i; ++j;
if(t[i] != t[j]) Next[i] = j;
else Next[i] = Next[j];
} else j = Next[j];
}
}
string s, t;
int main()
{
int cas = 0;
int TAT; scanf("%d", &TAT);
while(TAT -- )
{
cin >> t;
s = t + t;
int t_len = t.size(), s_len = s.size();
GetExtend(s, s_len, t, t_len);
kmp(t, t_len);
int cycL = t_len - Next[t_len], kind = 1;
if(Next[t_len] && t_len % cycL == 0)
kind = t_len / cycL;
int _min = 0, _equal = 0, _max = 0;
for(int i = 0; i < t_len; i ++ )
{
if(extend[i] == t_len)
++ _equal;
else
{
if(s[i + extend[i]] > t[extend[i]])
++ _max;
else
++ _min;
}
}
printf("Case %d: %d %d %d\n", ++ cas, _min / kind, _equal / kind, _max / kind);
}
return 0;
}
ONLY 扩展KMP的简单做法
- 上面的KMP讲真,真的做麻烦了~但是还好,总归复习了一下next[]的应用哈哈~
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 2000000 + 7;
int Next[maxN], extend[maxN];
void GetNext(string t, int len)
{
int front = 0, p = 0;
Next[0] = len;
for(int i = 1; i < len; i ++ )
{
if(i >= p || i + Next[i - front] >= p)
{
if(i >= p) p = i;
while(p < len && t[p] == t[p - i]) ++ p;
Next[i] = p - i;
front = i;
}else Next[i] = Next[i - front];
}
}
void GetExtend(string s, int s_len, string t, int t_len)
{
GetNext(t, t_len);
int front = 0, p = 0;
for(int i = 0; i < s_len; i ++ )
{
if(i >= p || i + Next[i - front] >= p)
{
if(i >= p) p = i;
while(p < s_len && p - i < t_len && s[p] == t[p - i]) ++ p;
extend[i] = p - i;
front = i;
}else extend[i] = Next[i - front];
}
}
string s, t;
int main()
{
int cas = 0;
int TAT; scanf("%d", &TAT);
while(TAT -- )
{
cin >> t;
s = t + t;
int t_len = t.size(), s_len = s.size();
GetExtend(s, s_len, t, t_len);
int _min = 0, _equal = 0, _max = 0;
for(int i = 0; i < t_len; i ++ )
{
if(extend[i] == t_len)
++ _equal;
else
{
if(s[i + extend[i]] > t[extend[i]])
++ _max;
else
++ _min;
}
}
printf("Case %d: %d %d %d\n", ++ cas, _min / _equal, 1, _max / _equal);
}
return 0;
}