吐槽:这题的输出真恶心恶心恶心恶心恶心*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;
}