小 Z 同学非常喜欢找事,现在有很多名为“事”的字符串,现在小 Z 想要找“事”,请你帮助他判断,他今天是否找了两件相同的事。
输入描述
一、普通遍历
本题最简单的思路就是这样想:判断一个字符串数组是否有重复的字符串,就相当于一个数组里是否存在重复的数字。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int n;
int main()
{
// 请在此输入您的代码
cin>>n;
string s[n];
for(int i=0;i<n;i++) cin>>s[i];
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(s[i]==s[j]){
cout<<'1';
return 0;
}
}
}
cout<<'0';
return 0;
}
这样看来蓝桥比赛的部分题还是可以得分的,对于第一次参加的同学来说,就按正常思路写,不用想太复杂。
不过,这个算法的时间复杂度是O(n^2),因为它使用了两个嵌套的循环来比较所有的字符串对。如果字符串数组很大,可能会导致较长的执行时间。但是还是能过一两个示例的。
二、哈希表统计
这道题涉及到重复的问题,自然而然就想到哈希表,这是哈希表的一个标志性特点,“哈希表去重”,记住这个关键词就好。
哈希表的一般写法如下:
#include <iostream>
#include <unordered_map>
int main() {
// 创建一个哈希表
std::unordered_map<int, std::string> hashTable;
// 向哈希表中插入键值对
hashTable.insert({1, "apple"});
hashTable.insert({2, "banana"});
hashTable.insert({3, "orange"});
// 查找特定的键值对
int key = 2;
auto iter = hashTable.find(key);
if (iter != hashTable.end()) {
std::cout << "Key " << key << " found. Value: " << iter->second << std::endl;
} else {
std::cout << "Key " << key << " not found." << std::endl;
}
// 遍历哈希表
for (const auto& pair : hashTable) {
std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl;
}
return 0;
}
像上面怎么创建怎么插入以及一些经常用到的代码是要记的。
另外补充一下:哈希表在C++中有两种实现方式:std::unordered_map
和std::map
。很明显能看出一个是有序,一个是无序。但都不允许重复。
unordered_map
和map我觉得最棒的一点是
键类型具有可哈希性,也就是说必须能够通过哈希函数将键映射到对应的桶中。C++中的内置类型(如整数、浮点数、字符、指针等)和标准库中提供的大多数类型都是可哈希的。而且我之前敲代码的时候发现它的键也就是数组的下标可以是字符串,还可以是负数!具体看一下代码:
#include <iostream>
#include <cmath>
#include <unordered_map>
using namespace std;
int main()
{
int n;
cin>>n;
unordered_map<string,int> p;
for(int i = 0; i < n; i++){
string temp;
cin>>temp;
p[temp] ++;
if(p[temp] > 1){
cout<<1;
return 0;
}
}
cout<<0;
return 0;
}
上面的unordered_map换成map也是可以的。
三、字典树
这个理解起来的话还挺难的,等遇到模板题,我再综合写一下题解。
//trie树,又叫字典树,前缀树
//主要有2种操作,存储字符串,查询字符串
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e5 + 10;
int son[N][26];
//字符串都是有小写字母组成,所以一个点,最多有26个子结点
//二维数组的行的范围是字符串大小的范围
//0表示根结点,为NULL, 如果该结点不存在,也是0
int cnt[N];
//存储的每个字符串,最后一个元素出现的次数-->该字符串出现的次数
int idx;
//每个结点都有独一唯一的idx
char str[N]; //字符串
void insert(char * str)
{
int p = 0;
//记录每个字符串的末尾元素-->记录该结点的idx
//也是二维数组的行
//字符串以 "/0" 结尾,可用做循环条件
for(int i = 0; str[i]; i++)
{
//以ASCII码记录 0~ 25
int u = str[i] - 'a';
//不存在这个结点,插入 (赋予结点唯一的idx)
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
//注意:这里不能用if else 语句,在做插入的时候,如果不存在,就插入新结点,更新下idx,要记录元素的idx
//存在,说明该结点不用插入,但还是要记录该结点的idx,区别于查询操作(可用if else 语句,就找到,找不到两种情况),当然查询操作也可不用
}
//记录该字符串出现的次数
cnt[p]++;
}
int check(int * cnt)
{
for(int i = 0; i < N; i++)
{
if(cnt[i] > 1)
{
return 1;
}
}
return 0;
}
int main()
{
int n;
scanf("%d", &n);
while(n--)
{
scanf("%s", str);
insert(str);
}
printf("%d", check(cnt));
return 0;
}