tire树暴力--找最短唯一的电话号码标识符

http://codeforces.com/problemset/problem/858/D---》原题

 一句话概括题意:在输入的所有电话号码中输出每一个号码的最短的特有的标识符(就是一段子串只在此电话号中有在其他号码中没有)

 看完题解大部分都用的什么时间戳,看不懂,就采用了复杂一点但好理解的,抵消-寻找-还原 的三步走战略。但是还是调了好长时间的bug,因为一般的tire树只需要将所有的字符串依次插入到tire树中一次便可,而此题需要将每个电话号的0~8位,1~8位,2~8位,,,,,7~8位,8位依次插入到tire树中,正因为忽略了此要点,所以tire数组开小了。看代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
struct Tire {
	int num;//记录树中每个节点出现的次数
	int next_s[15];
	Tire(){
		num=1;
		memset(next_s,-1,sizeof(next_s));
	}
}tire[1000000];//数组大小要远大于70000*9 
ll cnt=0;
char str[70005][15];
void insert(int s,int id){//传进的参数表示&str[id][s]. 
	int p=0;
	for(int i=s;str[id][i]!='\0';i++){
		if(tire[p].next_s[str[id][i]-'0']==-1){
			tire[p].next_s[str[id][i]-'0']=++cnt;
			p=cnt;
		}
		else{
			p=tire[p].next_s[str[id][i]-'0'];
			tire[p].num++;
		}
	}
}
ll LEN,BEGIN;
//寻找 
void find(ll s,ll id){
	int p=0;
	for(int i=s;str[id][i]!='\0';i++){
		//此if可有可无---剪枝 
		if(tire[p].next_s[str[id][i]-'0']==-1){
			return ;
		}
		p=tire[p].next_s[str[id][i]-'0'];
		if(tire[p].num==0){//抵消后第一个num==0的点说明此后缀的前缀是s~i独有的子串 
			if(i-s<LEN){//保留最小的LEN(更新or不更新) 
				LEN=i-s;
				BEGIN=s;
				return ;
			}
		}
	}
}
//抵消 
void work_1(ll s,ll id){
	int p=0;
	for(int i=s;str[id][i]!='\0';i++){
		if(tire[p].next_s[str[id][i]-'0']==-1){
			return ;
		}
		p=tire[p].next_s[str[id][i]-'0'];
		tire[p].num--;//将此后缀的前缀的子树的节点计数减一(将此前缀删掉) 
	}
}
//还原 
void work_2(ll s,ll id){
	int p=0;
	for(int i=s;str[id][i]!='\0';i++){
		if(tire[p].next_s[str[id][i]-'0']==-1){
			return ;
		}
		p=tire[p].next_s[str[id][i]-'0'];
		tire[p].num++;//将此后缀的前缀还原 
	}
}
int main(){
	ll n;
	scanf("%lld",&n);
	getchar();
	for(int i=0;i<n;i++){
		scanf("%s",str[i]);
		getchar();
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<str[i][j]!='\0';j++){
			insert(j,i);//构造后缀的前缀树 
		}
	}
	for(int i=0;i<n;i++){
		BEGIN=0;
		LEN=0x3f3f3f3f;
		for(int j=0;j<str[i][j]!='\0';j++){
			work_1(j,i);//删除str[i]的所有后缀的前缀子树 
		}
		for(int j=0;j<str[i][j]!='\0';j++){
			find(j,i);//寻找最小的LEN 
		}
		for(int j=0;j<str[i][j]!='\0';j++){
			work_2(j,i);//还原str[i]的所有后缀的前缀子树 
		}
		for(int j=BEGIN;j<=BEGIN+LEN;j++){
			cout<<str[i][j];//找到最小的子串的起点和长度后输出 
		}
		cout<<endl;
	}
	return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值