玲珑杯”ACM比赛 Round #4 1054 - String cut 暴力。学到了扫描的另一种思想
http://www.ifrog.cc/acm/problem/1054
问删除一个字符后的最小循环节是多少、
比赛的时候想不出,不知道怎么暴力。
赛后看了别人代码才晓得。唉,还以为自己字符串还不错,但是变了一点点就不懂了。
1、可以暴力枚举循环节长度i,必要条件是(lenstr - 1) % i == 0。这是必须的,因为必须是倍数,关于约数的个数:(不大)
http://vfleaking.blog.163.com/blog/static/174807634201341913040467/
2、怎么判断这个循环节可行呢?
比如我有Habcabcabc,枚举了3,那么怎么判断3是可行的呢?
关键是利用扫描的思想。
记录cnt表示匹配了多少个,那么只需匹配6个就证明可行。
第一,'H' != 'c',cnt设置为0,表明一个也没匹配成功。
第二步,'a'和'a'成功。然后第三步'b'和'b'成功,'c'和'c'成功,竟然能变成枚举了"abc",然后就这样一直枚举下去。
这和我以前写的不同,Hab|cab|cab|c,我以前的是很死板的分段,现在这个不但能满足要求(求出正常的解,就是abcabcabc这样一样能枚举3是ans),还能忽视某些值,例如这里忽视了H。。
所以说怎么用O(n)判断可不可行,也是有技巧的。(这个能求出满足循环节为3的最大长度)
但是还是不能满足题目,例如aabaa,只能求出是循环节是1的时候长度是2,怎么模拟删除一个字符呢?
答案是把字符串写两次。变成aabaa|aabaa。。就是相当于反向的字符串了,那么既然循环,反向或者旋转与否不影响。
例如aHbcabcabc|aHbcabcabc。
这个变成了bcabcabca。。ans = 3;
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #define IOS ios::sync_with_stdio(false) using namespace std; #define inf (0x3f3f3f3f) typedef long long int LL; #include <iostream> #include <sstream> #include <vector> #include <set> #include <map> #include <queue> #include <string> const int maxn = 100001 + 20; char str[maxn * 2]; void work() { int lenstr; cin >> lenstr; cin >> str + 1; int t = 1; for (int i = lenstr + 1; i <= 2 * lenstr; ++i) { str[i] = str[t++]; } str[2 * lenstr + 1] = '\0'; int ans = lenstr - 1; t = lenstr - 1; for (int i = 1; i <= t; ++i) { if (t % i != 0) continue; int cnt = 0; for (int j = 1; j + i <= 2 * lenstr; ++j) { if (str[j] == str[j + i]) { cnt++; if (cnt == t - i) { cout << t / i << endl; return; } } else cnt = 0; } } cout << 1 << endl; return; } int main() { #ifdef local freopen("data.txt","r",stdin); #endif IOS; int t; cin >> t; while (t--) work(); return 0; }
posted on 2016-11-05 20:39 stupid_one 阅读(240) 评论(0) 收藏 举报