Contact 联系
IOI'98
奶牛们开始对用射电望远镜扫描牧场外的宇宙感兴趣。最近,他们注意到了一种非常奇怪的脉冲调制微波从星系的中央发射出来。他们希望知道电波是否是被某些地外生命发射出来的,还是仅仅是普通的的星星发出的。描述
帮助奶牛们用一个能够分析他们在文件中记下的记录的工具来找到真相。他们在寻找长度在A到B之间(含)在每天的数据文件中重复得最多的比特序列 (1 <= A <= B <= 12)。他们在找那些重复得最多的比特序列。一个输入限制告诉你应输出多少频率最多的序列。
符合的序列可能会重叠,并且至少出现一次的序列会被计数。
格式
PROGRAM NAME: contact
INPUT FORMAT:
(file contact.in)
第一行: 三个用空格分隔的整数: A, B, N; (1 <= N < 50)
第二行及以后: 一个最多200,000字符的序列,全是0或1; 每行字符数不大于80。
OUTPUT FORMAT:
(file contact.out)
输出N个频率最高的序列(按照频率由高到低的次序)。由短到长排列频率相同的这些序列,如果长短相同,按二进制大小排列。如果出现的序列个数小于N,输出存在的序列。
对于每个存在的频率,先输出单独包含该频率的一行,再输出以空格分隔的这些序列。每行六个(除非少于六个剩下)。
SAMPLE INPUT
2 4 10 01010010010001000111101100001010011001111000010010011110010000000
在样例里,序列100出现了12次,而序列1000出现了5次。次数最多的序列是00,出现了23次。
SAMPLE OUTPUT
23 00 15 01 10 12 100 11 11 000 001 10 010 8 0100 7 0010 1001 6 111 0000 5 011 110 1000 4 0001 0011 1100
分析:
这题很简单。。。O(len*M)暴力hash就能过。。。开始SB了想暴力匹配O(len*M^2)。。我是怎么SB的我现在都不知道= =。。。然后就用字母树(Trie树)做的。。。len=1~12 M <=200000。
字母树方法:先建立一个满二叉字母树(结点是0和1)这样就把所有长度0、1的组合都记录下来了~~~然后对于每个长度扫描子串找出所有可能再统计个数。建立满二叉Trie树的时间复杂度是O(2^len * len),查询复杂度O(len * M)。。。(事实上也可以用AC自动机来做,原理一样。。。)好像不比hash快?= = 。。。事实上这道题我多余的用了Trie+hash。。。菜鸟对Trie树的掌握还是不够深啊。。。(于是有种把Trie树用错了的感觉= =。。。)
hash方法:写完这个我发现。。。字母树在这道题里完全是多余的= =。。。因为这题里是01串而且允许所有的01情况,这样的Hash是可以提前预处理出来的。。。比如这里子串长度最大12且允许所有情况,那么建立一个hash[5000][12]时已经建立了所有的情况。比如hash[1][3]=001,hash[0][3]=000,hash[1][2]=01。。。所以hash复杂度就是查询的复杂度,即O(len * M)。这题好像就不是Trie树的题= =。。。完全体现不出Trie树的优越性呐?~~还请高手指点下哇。。。
KMP方法:这个只是秀逗一下想出来的。。。这个好像复杂度好像是。。。O(len*M寻找子串然后*M)?。。。艾玛 O(len * M^2)。。。。。。事实证明KMP适合1个匹配多个,不适合多字符串匹配(还是用Trie和AC自动机吧T_T。。。)。。。
代码:
/*
ID:138_3531
LANG: C++
TASK: contact
*/
#include <iostream>
#include <cstring>
#include <fstream>
#include <string>
#include <cmath>
using namespace std;
int hash[5000][13];
const int branchNum = 26; //声明常量
int i;
char a[12];
struct Trie_node
{
bool isStr; //记录此处是否构成一个串。
Trie_node *next[branchNum]; //指向各个子树的指针,下标0-25代表26字符
Trie_node():isStr(false)
{
memset(next,NULL,sizeof(next));
}
};
class Trie
{
public:
Trie();
void insert(const char* word);
bool search(char* word);
void deleteTrie(Trie_node *root);
private:
Trie_node* root;
};
Trie::Trie()
{
root = new Trie_node();
}
void Trie::insert(const char* word)
{
Trie_node *location = root;
while(*word)
{
if(location->next[*word-'0'] == NULL) //不存在则建立
{
Trie_node *tmp = new Trie_node();
location->next[*word-'0'] = tmp;
}
location = location->next[*word-'0']; //每插入一步,相当于有一个新串经过,指针要向下移动
word++;
}
location->isStr = true; //到达尾部,标记一个串
}
bool Trie::search(char *word)
{
Trie_node *location = root;
while(*word && location)
{
location = location->next[*word-'0'];
word++;
}
return(location!=NULL && location->isStr);
}
void Trie::deleteTrie(Trie_node *root)
{
for(i = 0; i < branchNum; i++)
{
if(root->next[i] != NULL)
{
deleteTrie(root->next[i]);
}
}
delete root;
}
Trie t;
void buildtree(int n,int x)
{
if (n==x+1)
{
t.insert(a);
return;
}
for (int i=0;i<2;i++)
{
a[n]=i+'0';
buildtree(n+1,x);
a[n]='\0';
}
return;
}
int translate(char *tt,int l)
{
int num=0;
int w=0;
for (int i=0;i<l;i++)
{
num+=(tt[l-i-1]-'0')*pow(2,w);
w++;
}
return num;
}
int main()
{
ifstream fin("contact.in");
ofstream fout("contact.out");
int A,B,N;
fin>>A>>B>>N;
string s="";
string temp;
while (fin>>temp)
{
s=s+temp;
memset(a,0,sizeof(a));
}
int l=s.size();
for (int i=0;i<12;i++)
buildtree(0,i);
for (int i=A;i<=B;i++)
{
for (int j=0;j<=l-i;j++)
{
char tt[13]="";
for (int k=0;k<i;k++)
{
tt[k]=s[k+j];
}
if (t.search(tt))
{
int ll=strlen(tt);
hash[translate(tt,ll)][ll]++;
}
}
}
for (int i=0;i<5000;i++)//这里输出没有按题目要求做,嫌麻烦了。。。光验证结果对了。。。
for (int j=0;j<13;j++)
if (hash[i][j]>0)
cout<<i<<' '<<hash[i][j]<<endl;
return 0;
}