吐槽:这题的输出真恶心恶心恶心恶心恶心*10000……
TLE方法:
暴力KMP,把所有的100 101 111之类的方案,全部和原串匹配一次…… 然后排序输出。
Compiling... Compile: OK Executing... Test 1: TEST OK [0.008 secs, 9424 KB] Test 2: TEST OK [0.003 secs, 9424 KB] Test 3: TEST OK [0.032 secs, 9424 KB] Test 4: TEST OK [0.065 secs, 9424 KB]
> Run 5: Execution error: Your program (`contact') used more than the allotted runtime of 1 seconds (it ended or was stopped at 1.674 seconds) when presented with test case 5. It used 9420 KB of memory. |
/* TASK:contact LANG:C++ */ #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; bool text[200020]; int L, R, n, tlen=0, slen, next[50]; bool son[50]; void init() { scanf("%d%d%d\n", &L, &R, &n); char ch; while (1) { ch = getchar(); if (ch == '\n') continue; if (ch == EOF) break; text[tlen++] = ch - '0'; } } void cal(int k, int w) //k这个数字,转化为w位数字 { slen = w - 1; memset(son,0,sizeof(son)); while (k) { son[slen--] = k % 2; k /= 2; } slen = w; } int kmp(bool *son, bool *text, int slen, int tlen) { int i = 0, j = -1; int ans = 0; next[0] = -1; while (i != slen) { if (j == -1 || son[i] == son[j]) next[++i] = ++ j; else j = next[j]; } i = 0, j = 0; while (i != tlen) { if (j == -1 || text[i] == son[j]) ++i, ++ j; else j = next[j]; if (j == slen) { ++ ans; j = next[j]; } } return ans; } struct pack { int key; int k, w; }a[500000]; int tail=0; bool operator < (pack A, pack B) { if (A.key != B.key) return A.key > B.key; //首先是重复次数多的 if (A.w != B.w) return A.w < B.w; if (A.k != B.k) return A.k < B.k; } void doit() { for (int i = L; i <= R; ++ i)//穷举位数 { for (int j = 0; j <= (1 << i) - 1; ++ j)//穷举十进制数字,然后换成二进制 { cal(j, i);// a[tail].key = kmp(son, text, slen, tlen); if (a[tail].key == 0) continue; a[tail].k = j; a[tail].w = i; ++tail; } } sort(a, a + tail); int tmp = -1,t=0, now=0; //最后一次出现的循环次数的数值, 输出了多少个不同的循环, 当前节点 int output_num; while (now != tail) { if (a[now].key != tmp)//新的重复次数 { if (t == n) break; if (tmp != -1) printf("\n"); printf("%d", tmp = a[now].key); putchar('\n'); ++ t; output_num = 0; }else{ if (output_num != 6) putchar(' '); else { putchar('\n'); output_num = 0; } } cal(a[now].k, a[now].w); for (int i = 0; i != a[now].w; ++ i) putchar(son[i]+'0'); ++ output_num; ++now; } putchar('\n'); } int main() { freopen("contact.in","r",stdin); freopen("contact.out","w",stdout); init(); doit(); return 0; }
AC方法1:
位运算。 把原串看成是一个二进制串。 把后面1位,2位,3位…… 都截取下来,换成十进制K,然后给b[k][i] (十进制是K,二进制是i位),保存进b数组,然后给b数组排序输出即可。
要点1: 0 00 000是不一样的串, 所以可以要记录一个数字到底是几位。
要点2: 为了省事,在给sort附加CMP信息的时候,可以在3个关键字上做手脚,达到排序后直接输出即可,不需要再做任何调整。 (第一关键字正序,第二第三关键字倒序)
速度很优秀
Executing... Test 1: TEST OK [0.003 secs, 9816 KB] Test 2: TEST OK [0.000 secs, 9816 KB] Test 3: TEST OK [0.003 secs, 9816 KB] Test 4: TEST OK [0.003 secs, 9816 KB] Test 5: TEST OK [0.005 secs, 9816 KB] Test 6: TEST OK [0.019 secs, 9816 KB] Test 7: TEST OK [0.014 secs, 9816 KB] All tests OK.
/* TASK:contact LANG:C++ */ #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; int L, R, n, slen, tlen=0; int son[13]; inline void cal(int k, int w) //k这个数字,转化为w位数字 { slen = w - 1; memset(son,0,sizeof(son)); while (k) { son[slen--] = k % 2; k /= 2; } slen = w; } struct pack { int key; //出现的次数 int k, w; //模拟的是k,位数是w }a[140000]; int tail=0; int b[9000][15]={0}; inline bool operator < (pack A, pack B) { if (A.key != B.key) return A.key > B.key; //首先是重复次数多的 if (A.w != B.w) return A.w < B.w; if (A.k != B.k) return A.k < B.k; } void doit() { for (int i = L; i <= R; ++ i) { for (int j = 0; j <= (1 << i) - 1; ++ j) { if (b[j][i] == 0) continue; a[tail].key = b[j][i]; a[tail].w = i; a[tail++].k = j; } } sort(a, a + tail); int tmp = -1,t=0, now=0; //最后一次出现的循环次数的数值, 输出了多少个不同的循环, 当前节点 int output_num; while (now != tail) { if (a[now].key != tmp)//新的重复次数 { if (t == n) break; if (tmp != -1) printf("\n"); printf("%d", tmp = a[now].key); putchar('\n'); ++ t; output_num = 0; }else{ if (output_num != 6) putchar(' '); else { putchar('\n'); output_num = 0; } } cal(a[now].k, a[now].w); for (int i = 0; i != a[now].w; ++ i) putchar(son[i]+'0'); ++ output_num; ++now; } putchar('\n'); } inline void check(int tot, int wei) //tot这个数字,来检测所有wei位(二进制下)的数字 { int s = (1 << wei); int t = tot & ((1 << wei) - 1); // tot的xxx b[t][wei] ++; } int main() { freopen("contact.in","r",stdin); freopen("contact.out","w",stdout); scanf("%d%d%d\n", &L, &R, &n); char ch; int tot = 0; while (1) { ch = getchar(); if (ch == '\n') continue; if (ch == EOF) break; ++tlen; tot <<= 1; tot += ch=='0'?0:1; //新的数字插入 for (int i = L; i <= min(R, tlen); ++ i) check(tot, i); //tot长度为i } doit(); return 0; }
方法3: AC自动机
因为AC自动机拥有多串匹配功能,所以嘛~ 把长度范围内的二进制串都放进trie里,然后直接把后来的串放在AC自动机里跑一下就行了~ 速度也非常快。2个程序在速度差不多…… 方法4,我实在想不到了……别人程序比我慢,我猜是getchar的区别导致的
Compiling... Compile: OK Executing... Test 1: TEST OK [0.008 secs, 5676 KB] Test 2: TEST OK [0.008 secs, 5676 KB] Test 3: TEST OK [0.011 secs, 5932 KB] Test 4: TEST OK [0.008 secs, 5676 KB] Test 5: TEST OK [0.024 secs, 5932 KB] Test 6: TEST OK [0.032 secs, 5676 KB] Test 7: TEST OK [0.032 secs, 5932 KB] All tests OK.
/* TASK:contact LANG:C++ */ #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <queue> #include <cstring> using namespace std; int L, R, n, slen, tlen=0; int son[13]; inline void cal(int k, int w) //k这个数字,转化为w位数字 { slen = w - 1; memset(son,0,sizeof(son)); while (k) { son[slen--] = k % 2; k /= 2; } slen = w; } struct pack { int key; //出现的次数 int k, w; //模拟的是k,位数是w }a[140000]; int tail=0; int b[9000][15]={0}; inline bool operator < (pack A, pack B) { if (A.key != B.key) return A.key > B.key; //首先是重复次数多的 if (A.w != B.w) return A.w < B.w; if (A.k != B.k) return A.k < B.k; } void doit() { for (int i = L; i <= R; ++ i) //排序 { for (int j = 0; j <= (1 << i) - 1; ++ j) { if (b[j][i] == 0) continue; a[tail].key = b[j][i]; a[tail].w = i; a[tail++].k = j; } } sort(a, a + tail); int tmp = -1,t=0, now=0; int output_num; while (now != tail) //恶心输出 { if (a[now].key != tmp) { if (t == n) break; if (tmp != -1) printf("\n"); printf("%d", tmp = a[now].key); putchar('\n'); ++ t; output_num = 0; }else{ if (output_num != 6) putchar(' '); else { putchar('\n'); output_num = 0; } } cal(a[now].k, a[now].w); for (int i = 0; i != a[now].w; ++ i) putchar(son[i]+'0'); ++ output_num; ++now; } putchar('\n'); } struct node { int flag; int k, w; node *fail; node *c[2]; node() { flag = k = w = 0; fail = NULL; c[0] = c[1] = NULL; } }; struct AC_DFA { int size; queue<node*>q; node *root; AC_DFA() { root = new node; } inline void ins(int *word, int len, int k) { node *now = root; for (int i = 0; i != len; ++ i) { if (!now -> c[word[i]]) now -> c[word[i]] = new node; now = now -> c[word[i]]; } now -> flag = 1; //这里有单词 now -> k = k; // now -> w = len; } void setfail() { q.push(root); root -> fail = NULL; while (!q.empty()) { node *now = q.front(); q.pop(); for (int i = 0; i != 2; ++ i) { if (now -> c[i]) { node *p = now -> fail; while (p && !p -> c[i]) p = p -> fail; now -> c[i] -> fail = p ? p -> c[i] : root; q.push(now -> c[i]); }else now -> c[i] = now == root ? root : now -> fail -> c[i]; } } } }ac; void init() { scanf("%d%d%d\n", &L, &R, &n); for (int i = L; i <= R; ++ i) { for (int j = 0; j != (1 << i) ; ++ j) { cal(j, i); ac.ins(son, i, j); } } ac.setfail(); } int main() { freopen("contact.in","r",stdin); freopen("contact.out","w",stdout); init(); char ch; node *now = ac.root, *tmp; while (1) { ch = getchar(); if (ch == '\n') continue; if (ch == EOF) break; now = now -> c[ch - '0']; tmp = now; while (tmp && tmp -> flag) { b[tmp -> k][tmp -> w] ++; tmp = tmp -> fail; } } doit(); return 0; }