【打表】第一个只出现一次的字符与字符流内实现

面试题50:第一个只出现一次的字符

在字符串中找出第一个只出现一次的字符。如输入"abaccdeff",则输出’b’。

输入一共就256种ASCII字符,第一遍扫描把出现次数记到数组里,第二遍边扫描边到数组里去找。

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

//输入字符数组地址,输出寻找到的第一个只出现一次的字符 
char FirstNotRepeatingChar(const char* pString) {
	if(pString == nullptr)//空串没有只出现一次的字符
		//实际上它也是个ASCII字符,但以其作为字符串结尾
		return '\0';//在这种情况下,也确实可以认定'\0'是第一次出现一次的 

	const int tableSize = 256;//字符表大小 
	unsigned int hashTable[tableSize];//创建字符表,字符出现次数不能为负 
	for(unsigned int i = 0; i < tableSize; ++i)//全部初始化为出现0次 
		hashTable[i] = 0;

	const char* pHashKey = pString;//指针从字符串首开始 
	while(*(pHashKey) != '\0')//第一次遍历整个字符串,出现次数记录到表中 
		hashTable[*(pHashKey++)] ++;//hashTable[*pHashKey]++,pHashKey++

	pHashKey = pString;//再次从头开始 
	while(*pHashKey != '\0') {//遍历整个字符串 
		if(hashTable[*pHashKey] == 1)//找到第一个出现一次的字符 
			return *pHashKey;//将其直接返回 
		//当前字符不止出现一次,继续找下一个 
		pHashKey++;
	}
	//没找到只出现一次的字符 
	return '\0';//也可以认为是串结束符一定只出现一次 
}

int main() {
	cout<<FirstNotRepeatingChar("google")<<endl;//l
	return 0;
}
面试题50变种:字符流中第一个只出现一次的字符

一样的题目,只是从字符流中依次读出时随时可能要到目前为止读出的字符中第一个不重复的字符,这时如果遍历"到目前为止的子串"然后用第一种方法会很麻烦,因为从字符流中读取,子串也可能是非常大的。可以调整表的记录方式,仅仅去表中查看记录的状态而不需要查看字符串就能确定当前所求的是哪个字符。

这样每次查找当前的第一个只出现一次的字符,时间复杂度是O(1)而不是O(m),m是当前子串的长度。因为ASCII字符种类256是一个确定的数,哈希表总长不变。

原题是记录数字,然后再次遍历字符串去确定位置;现在改成直接记录位置,因为位置作为数组下标一定是非负的,可以用负数去表示"没出现过"以及"超过了一次"。

这两种状态要用不同的负数来表示,它们是不能合并的状态:
在这里插入图片描述

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

//字符统计类 
class CharStatistics {
	public:
		//构造器初始化
		CharStatistics() : index(0) {//读入的下标从0开始 
			for(int i = 0; i < 256; ++i)
				occurrence[i] = -1;//初始化所有字符为"未出现" 
		}

		//读入一个字符ch时,更新状态表 
		void Insert(char ch) {
			if(occurrence[ch] == -1)//没读入过->读入了,记录读入到的下标 
				occurrence[ch] = index;
			else if(occurrence[ch] >= 0)//读入过->出现超过一次 
				occurrence[ch] = -2;
			index++;//读下一个位置的字符,位置+1 
		}

		//找出读到当前位置的第一次出现一次的字符 
		char FirstAppearingOnce() {
			char ch = '\0';//找不到就是'\0' 
			//编译器允许的最大int 
			int minIndex = numeric_limits<int>::max();
			//遍历哈希表,找到最小的那个非负数(出现位置)对应的字符 
			for(int i = 0; i < 256; ++i) {
				//非负(状态② )且小于当前找到的最小(不可能出现等于的情况) 
				if(occurrence[i] >= 0 && occurrence[i] < minIndex) {
					ch = (char) i;//更新字符 
					minIndex = occurrence[i];//更新最小下标 
				}
			}
			return ch;
		}

	private:
		// occurrence[i]: A character with ASCII value i;
		// occurrence[i] = -1: The character has not found;
		// occurrence[i] = -2: The character has been found for mutlple times
		// occurrence[i] >= 0: The character has been found only once
		int occurrence[256];//哈希表,occurrence是出现的意思 
		int index;
};

int main() {
	CharStatistics cs;
	cs.Insert('g');
	cout<<cs.FirstAppearingOnce()<<endl;//g
	cs.Insert('o');
	cout<<cs.FirstAppearingOnce()<<endl;//g
	cs.Insert('o');
	cout<<cs.FirstAppearingOnce()<<endl;//g
	cs.Insert('g');
	cout<<cs.FirstAppearingOnce()<<endl;//
	cs.Insert('l');
	cout<<cs.FirstAppearingOnce()<<endl;//l
	cs.Insert('e');
	cout<<cs.FirstAppearingOnce()<<endl;//l
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值