HDU 1251 统计难题

统计难题

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others)
Total Submission(s): 69742 Accepted Submission(s): 24131

Problem Description
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).

Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.

注意:本题只有一组测试数据,处理到文件结束.

Output
对于每个提问,给出以该字符串为前缀的单词的数量.

Sample Input
banana
band
bee
absolute
acm

ba
b
band
abc

Sample Output
2
3
1
0

  • 题意:
    统计后一组中每个字符串在前一组中出现的次数,并输出。

  • 思路:
    字符串匹配问题,可以用map,字典树,字典树又可以用指针和数组来实现,但是这个题用指针字典树会空间超额。想看更详细的思路,就点开这个神奇的链接吧。
    题解(一位大佬的)

    把二维表格打出来了
    下面来看详细代码:map实现

#include<bits/stdc++.h>
using namespace std;

int main(){
	char str[10];
	map<string,int>m;
	while(gets(str)){
		int len = strlen(str);
		if(!len) break;   //输入了一个空行 
		for(int i=len; i>0; i--){   
			str[i] = '\0';//从后往前删除这个字符串,得到前缀 
			m[str]++;    //统计前缀的数量 
		}
	}
	while(gets(str))
	cout<<m[str]<<endl;
	return 0;
}

数组实现

#include<cstring>
#include<cstdio> 
using namespace std;
const int N = 1e6 +10; 

int trie[N][26];    //字典树数组,很巧妙地储存 

/*
这个二维数组,在每一行中存的元素,都是在同一个深度上的根,从而可以达到分支的目的 
每一列中的元素,则是代表每棵树的深度
这样行和列组合起来,就可一很好的确定树的分支了 
自己课一用 for 循环 把数组打印出来看一下 
思路中的第二个链接打印出的数组,可以看一下 
*/ 


int num[N] = {0};   //统计每个字符串出现的个数 
int pos =1;
char str[N];        //输入存样例用的 

void Insert(char str[]){    //插入建树 
	int p = 0;   //先从零行开始 
	for(int i=0; str[i]; i++){ //一直遍历到最后 
		int n= str[i] - 'a';   //转换成 int 存到数组当中   
		if(trie[p][n] == 0)    //判断是否存入数组中 
		trie[p][n] = pos++;    //pos 记录存的字符是第几个  
		p = trie[p][n];        //并且配和p来换行 
		num[p]++;              //统计字第几个字符出现的次数 
	}
}

int Find(char str[]){        //找的话就简单了,但也是很巧妙的
	int p = 0;
	for(int i=0; str[i]; i++){    //for循环,把第二组样例的每一个字符串的字符从头到尾编译一遍
		int n = str[i] - 'a';
		if(trie[p][n] == 0){        
			return 0; 
		}
		p = trie[p][n];       //一直跑到最后,得出最后一个字符是第几个输入的(第几个输入和建树有很大的关系,在插入中可以好好的模拟一下,也可以点开链接看一下)
	}
	return num[p];   返回此时的字符串出现的次数;
}

int main(){
	while(gets(str)){
		if(str[0] == '\0')
		break;
		Insert(str);
	}
	while(~scanf("%s",str))	{
		printf("%d\n",Find(str));
	}
	return 0;
}

指针实现

#include<bits/stdc++.h>
using namespace std;

struct Trie{            //字典树的定义 
	Trie *next[26];
	int num;               //以当前字符串为前缀的单词的数量 
	Trie(){                         //构造函数 
		for(int i=0; i<26; i++) next[i] = NULL;
		num = 0;
	}
};

Trie root;
void Insert(char str[]){             //将字符串插入到字典树中    
	Trie *p = &root;
	for(int i=0; str[i]; i++){       //遍历每一个字符 
		if(p -> next[str[i] - 'a'] == NULL)  //如果该字符没有对应的节点 
		p -> next[str[i] - 'a'] = new Trie;  //创建一个 
		p = p -> next[str[i] - 'a'];
		p -> num++; 
	}
}

int Find(char str[]){
	Trie *p = &root;
	for(int i=0;str[i];i++){
		if(p -> next[str[i] - 'a'] == NULL)
		return 0;
		p = p-> next[str[i] - 'a']; 
	}
	return p -> num;
}

int main(){
	char str[11];
	while(gets(str)){
		if(!strlen(str)) break;    //输入了一个空行 
		Insert(str);
	}
	while(gets(str)) cout<<Find(str)<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值