Codeforces #187 (Div. 1) B. Sereja and Periods && Hihocoder 1355 (字符串匹配倍增好题)

来源:互联网 发布:修复dll软件 编辑:程序博客网 时间:2024/06/05 06:07

题目链接:http://codeforces.com/contest/314/problem/B

题意:给定一个两个字符串stra, strb,和两个数c1,c2,求stra * c1 中有多少个字符序列 strb * c2。 a,b字符串的长度100, c1和c2小于1e7。

思路:用nxt[N][2]建立一个匹配数组,nxt[pos][0], 表示从pos开始匹配,匹配结束以后一共匹配了多少个字符序列,nxt[pos][1]表示从pos开始匹配,匹配结束以后匹配到哪个位置,这样就可以用快速幂一样匹配K次,用倍增快速算出来,一共匹配了多少个串,和最终的位置在哪。

代码:

 

 #include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <iostream>#include <string>#include <cmath>#include <vector>#include <set>#include <map>using namespace std;#define REP(i, a, b) for (int i = (a), i##_end_ = (b); i < i##_end_; ++i)#define MP make_pair#define PB push_back#define SZ(x) (int((x).size()))#define ALL(x) (x).begin(), (x).end()#define X first#define Y secondtemplate<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }template <class T>  inline bool RD(T &ret) {          char c; int sgn;          if (c = getchar(), c == EOF) return 0;          while (c != '-' && (c<'0' || c>'9')) c = getchar();          sgn = (c == '-') ? -1 : 1 , ret = (c == '-') ? 0 : (c - '0');          while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');          ret *= sgn;          return 1;  }  template <class T>  inline void PT(T x) {          if (x < 0) putchar('-') ,x = -x;          if (x > 9) PT(x / 10);          putchar(x % 10 + '0');  }const int INF = 0x3f3f3f3f;typedef long long LL;typedef pair<int, int> pii;typedef long double LD;const int N = 2e4 + 100;int go[N][2];int nxt[N][2];int x, y, lena, lenb;char a[N], b[N];LL quick(int n) {LL ans = 0;int pos = 0;while(n) {if(n & 1) {ans += go[pos][0];pos = go[pos][1];}n >>= 1;for(int i = 0; i < lenb; i ++) {nxt[i][0] = go[i][0] + go[go[i][1]][0];nxt[i][1] = go[go[i][1]][1];}swap(nxt, go);}return ans;}int main() {cin >> x >> y;scanf("%s%s", a, b);lena = strlen(a), lenb = strlen(b);int cur = 0;for(int i = 0; i < lenb; i ++) {cur = i;for(int p = 0; p < lena; p ++) {if(a[p] == b[cur]) {cur ++;if(cur == lenb) {cur = 0;   go[i][0] ++;}}}go[i][1] = cur;}LL ans = quick(x);printf("%lld\n", (ans / y));}


Hiho题目链接:http://hihocoder.com/problemset/problem/1355

题目:

描述

小Ho忘了做英语作业,被老师罚抄某段文本N遍。抄写用的作业纸每行包含M个格子,每个格子恰好能填写一个字符或者空格。抄写过程中单词不能跨行,如果某行剩余的格子不足以写完一个单词,那么这个单词需要写在下一行。单词间的空格不能省略。

例如在M=9的作业纸上写2遍"Good good study day day up":

123456789Good good study   day day   up Good  good     study day day up  

小Ho想知道当他抄写完N遍以后,最后一个字符在第几行、第几列。

输入

第一行包含两个整数N和M。  

第二行包含要抄写的文本。文本只包含大小字母和空格,并且单词之间只有一个空格。

对于40%的数据,1 <= N, M <= 1000  

对于100%的数据,1 <= N, M <= 1000000000, M >= 文本中最长的单词的长度,文本长度不超过100个字符。

输出

最后一个字符的行号和列号。

样例输入
2 9Good good study day day up
样例输出
7 7


思路:

对于M很大,明显可以感觉到可以一行写很多单词。所以对于M >= 1e5,每行可以至少抄1e5 / MAX_LEN = 1e3遍。

所以对于N = 1e9, 最多只需要换行N / 1e3 = 1e6次。每次换行的时候暴力抄一遍就好。

对于M < 1e5的时候,可以建立一个nxt[M][2]数组,表示抄完一遍一共用了多少行和最后字母的位置在哪。这样对于N = 1e9可以倍增暴力抄N遍。复杂度O(1e5 * log(N) )



0 0