KMP&&TRIE解答

POJ 1961

求最下循环节问题,上节课讲过了,没有理解的话,记住公式.

#include<iostream>
#include<cstring>
using namespace std;
string s;
int nxt[1000006];
int len;
int sim=1;
void get_next()
{
	nxt[0]=-1;
	int i=0,k=-1;
	while(i<s.size())
	{
		if(k==-1||s[i]==s[k])
		{
			i++;
			k++;
			nxt[i]=k;
		}
		else k=nxt[k];
	}
}
int main()
{
	while(cin>>len)
	{
		if(len==0)return 0;
		cin>>s;
		get_next();
		cout<<"Test case #"<<sim++<<endl;
		for(int i=2;i<=len;i++)
		{
			int lens=i;
			if(lens%(lens-nxt[i])==0&&nxt[i]!=0)//这里就是公式 当可以整除表明存在最小循环节
			{
				cout<<lens<<" "<<lens/(lens-nxt[i])<<endl;	
			}
		}
		cout<<endl;
	}
	return 0;
}

POJ1056

题目大意:就是输多组0 1数据,任意一组数据不能成为另一种的前缀
思路:建立trie树,每次插入的时候查询一下是否会有新字符要插入,如果有就符合,没有的话当前要插入的字符串就是其他字符串的前缀。
注意这道题数据很小:
0000
010
10
01
应该输出------not---------

#include<iostream>
#include<algorithm>
using namespace std;
const int N=100006;
string s;
int cnt[N];
int trie[N][3];
int index;
int k=1;
int f=1;
void init()
{
	f=1;
	index=0;
	for(int i=0;i<=N;i++)
	{
		cnt[i]=0;
		for(int j=0;j<3;j++)
		trie[i][j]=0;	
	}	
}
int insert(string t)
{
	int p=0;
	for(int i=0;i<t.size();i++)
	{
		int c=t[i]-'0';
		if(!trie[p][c])trie[p][c]=++index;
		p=trie[p][c];
		if(cnt[p])return 0;
	}
	cnt[p]++;
	return 1;
}
int query(string t)
{
	int p=0;
	for(int i=0;i<t.size();i++)
	{
		int c=t[i]-'0';
		if(!trie[p][c])return 1;//有新单词要插入 返回1
		p=trie[p][c];
		if(cnt[p])return 0;//当前字符串在之前已经有了返回0
	}
	return 0;//如果这个字符串是之前的前缀 返回0
}
int main()
{
	while(cin>>s)
	{
		if(s=="9")
		{
			if(f)cout<<"Set "<<k++<<" is immediately decodable"<<endl;
			else cout<<"Set "<<k++<<" is not immediately decodable"<<endl;
			init();	
			continue;
		}
		if(f)
		{   
			f=query(s);
			insert(s);		
		}		
	}
	return 0;
}

POJ2001

题目大意:找字符串的最独一无二的前缀
思路:trie树,建立的时候每个节点都打上标记,查询的时候碰到标记值为1的时候就是独一无二的前缀。碰到标记值大于1的进行下一次循环。

#include<iostream>
#include<string>
#include<stdio.h>
using namespace std;
const int N=2e6+5;
int trie[N][27];
int cnt[N];
int index;
char p[N][50];
void insert(string s)
{
		int p=0;
		for(int i=0;i<s.size();i++)
		{
			int c=s[i]-'a';
			if(!trie[p][c])trie[p][c]=++index;
			p=trie[p][c];
			cnt[p]++;
		}
		
}
void query(string s)
{
		int p=0;
		for(int i=0;i<s.size();i++)
		{
			int c=s[i]-'a';
			p=trie[p][c];
			if(cnt[p]>1)cout<<s[i];
			if(cnt[p]==1){cout<<s[i];return;}
		}
}
int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int line=0;
	while(scanf("%s",p[line])!=EOF)/用C的语法写,不然会tle 血的教训
	{
		line++;
		insert(p[line-1]);
	}
	for(int i=0;i<line;i++)
	{
		cout<<p[i]<<" ";
		query(p[i]);
		cout<<"\n";
	
	}
	return 0;
}

POJ2503

题目大意:给你一个翻译后的字符串和翻译前的字符串形成字典,输入翻译前的字符串将它进行翻译。
思路:建立trie树,节点值不仅要存标记值,而且还要存进去翻译后的字符串。(注意要用空间建立,第一次写没看见题目给定的范围用数组模拟空间会爆内存)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<map>
using namespace std;
const int N=46;
char op[1000000];
struct trieNode{
	int c;
	trieNode** son;
	int cnt;
	string ans;
	trieNode(int num)
	{
		ans="";
		c=num;
		cnt=0;
		son=new trieNode*[N];
		for(int i=0;i<N;i++)son[i]=NULL;
	}
};
trieNode* root;
void insert(string s,string s1)
{
	trieNode* p=root;
	for(int i=0;i<s.size();i++)
	{
		int c=s[i]-'a';
		if(!p->son[c])p->son[c]=new trieNode('c');
		p=p->son[c];
	}	
	p->cnt++;
	p->ans=s1;
}
string query(string s)
{
	trieNode* p=root;
	for(int i=0;i<s.size();i++)
	{
		int c=s[i]-'a';
		if(!p->son[c])return "";
		p=p->son[c];		
	}
	return p->ans;
}
int main()
{	
	root=new trieNode(0);	
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	string s1,s2;
	char t;
	while(true)
	{
		t=getchar();
		s1="";s2="";
		int flag=1;
		if(t=='\n')break;
		s1+=t;
		while(true)
		{
			t=getchar();
			if(t==' '){flag=0;continue;}
			if(t=='\n')break;
			if(flag)s1+=t;
			else s2+=t;			
		}
		insert(s2,s1);
	}
	while(scanf("%s",op)!=EOF)//用C写法,不然TLE wa哭了qvq
	{
		string pri=query(op);
		if(pri!="")
		cout<<pri<<endl;
		else cout<<"eh"<<endl;
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值