C++“窗口”法尋找子串

任務

題目和解題方法來源:左神(左程雲):深入解析字節跳動算法面試題與數據結構
用C++實現算法(原講解爲Java)。並通過隨機新建字符串進行測試。
源代碼於GitHub

題目

給定長度爲m的字符串aim,以及一個長度爲n的字符串str
問能否在str中找到- -個長度爲m的連續子串, 使得這個子串剛好由aim的m個字符組成,順序無所謂, 返回任意滿足條件的一個子串的起始位置,未找到返回-1

思路

基本方法:遍歷

找到str中所有字串substr,比較substr與aim是否成分相同(字母的種類和數量一致)

  • 統計第一個串 各字符統計
  • 遍歷第二個串 – 至0 則false

窗口法

遍歷所有可能的字串長度通過窗口:欠與還

解題

遍歷

//比較與aim長度相同的的所有子串是否含有與它成分一致,若有,返回偏移量,否則返回-1
int findAim(string str, string aim)
{
	//cout << str.length() - aim.length() << endl;
	if (str.length() < aim.length())
		return -1;
	
	//數組,統計字符範圍內的字符出現個數
	
	string substr;
	bool findAim;

	for (int i = 0; i < str.length() - aim.length(); i++)
	{
		int count[255] = {0};
		for (int i = 0; i < aim.length(); i++)
		{
			count[aim[i]]++;
		}
		substr = str.substr(i, aim.length());
		findAim = true;
		for (int i = 0; i < aim.length(); i++)
		{
			if (--count[substr[i]] < 0)
				findAim = false;				
		}
		if (findAim == true)
			return i;
	}
	return -1;
}

窗口法

//比較與aim長度相同的的所有子串是否含有與它成分一致,若有,返回偏移量,否則返回-1
//法2:最優解,通過“窗口”
int findAim2(string str, string aim)
{
	//cout << str.length() - aim.length() << endl;
	if (str.length() < aim.length())
		return -1;

	//初始爲aim的各字符值
	int count[255] = { 0 };
	int* pcnt = count + 97;
	for (int i = 0; i < aim.length(); i++)
		count[aim[i]]++;
	
	//記錄窗口與aim的交易關係
	//初始值是字符總長度
	int owe = aim.length();

	//初始化第一個窗口
	for (int i = 0; i < aim.length(); i++)
	{
		//若新增的是還在需要中的,那麼相當於“火中送碳”,owe--
		if (count[str[i]]-- > 0)
			owe--;
		//若新增的是之前冗餘不被需要的,那麼相當於“供非所求”,owe++
		else
			owe++;
	}
	if (owe == 0)
		return 0;

	//移動“窗口”,每次移動:拿去一個字符,新增一個字符,共可移動str.length() - aim.length()次
	//移除的字符是第i個,新增的字符是第i+aim.length()個
	for (int i = 0; i <= str.length() - aim.length(); i++)
	{
		//字符
		int removed = str[i], added = str[i + aim.length()];
		//判斷
		//若被移除的是之前冗餘不被接受的,那麼相當於“及時止損”,owe--
		if (count[removed]++ < 0)
			owe--;
		//若被移除的是還在需要中的,那麼相當於“屋漏偏逢連夜雨”,owe++
		else
			owe++;

		//若新增的是還在需要中的,那麼相當於“火中送碳”,owe--
		if (count[added]-- > 0)
			owe--;
		//若新增的是之前冗餘不被接受的,那麼與最初發生的事情一致,owe++
		else
			owe++;

		//全部抵消,世界和平,移動了i次窗口,位於第i+1處
		if (owe == 0)
			return i + 1;
	}
	return -1;
}

測試

生成隨機字符串

string generateArr(int len, int left, int right)
{
	if (left > right)
		return "";
	string str;// = "I lsfjo st";
	str.resize(len+1);

	for (int i = 0; i < len ; i++)
	{
		str[i] = left + rand() % (right - left);
		//cout<<str[i] << endl;
	}
	
	return str;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章