字符串的排列
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
示例1:
输入: s1 = "ab" s2 = "eidbaooo" 输出: True 解释: s2 包含 s1 的排列之一 ("ba").
示例2:
输入: s1= "ab" s2 = "eidboaoo" 输出: False
注意:
- 输入的字符串只包含小写字母
- 两个字符串的长度都在 [1, 10,000] 之间
解题思路:
解决这个问题前可以先回顾下无排列的子串匹配问题,我们当时使用的是实现了DFA状态转移图,当匹配到不同字符时来到不同的状态,知道最终完全匹配。
(图后补,下次一定)
而对于有排列的子串问题,我们一样可以构建一个DFA图来实现它,在这时候相比于无排列子串匹配问题,子串的字符顺序就不重要了,思路如下:
(图后补,下次一定)
逐个判断长字符串字符:
(1)不是子串字符,跳过
(2)是子串字符且没多余重复,记录当前匹配到的长度
(3)是子串字符但是已经多余了,比如子串“adc”,串“dcda“,第二个d是超出了子串d个数的,从第二个d这个位置往前重新匹配到dc。
(4)若匹配到的个数已经达到子串长度,完成。
class Solution { public: bool checkInclusion(string s1, string s2) { int len_s1 = s1.length(); int len_s2 = s2.length(); if(len_s1>len_s2) return false; if(len_s1==0) return true; if(len_s1==1){ for(int i=0;i<len_s2;i++){ if(s1[0]==s2[i]) return true; } return false; } int size[26];//子串字符统计 int size_tmp[26];//临时子串已用字符统计 memset(size,0,sizeof(int)*26); memset(size_tmp,0,sizeof(int)*26); map<char,int> mp; for(int i=0;i<len_s1;i++){ mp.insert(pair<char,int>(s1[i],i));//map容器储存,方便查找 size[s1[i]-'a']++;//记录个数 } int len=0; for(int i=0;i<len_s2;i++){ //不是子串字符重置跳过 if(mp.find(s2[i])==mp.end()){ len=0; memset(size_tmp,0,sizeof(int)*26); continue; } //是子串字符,判断是否多余出现 // cout<<s1[j]<<"--"<<s2[i]<<endl; if(size_tmp[s2[i]-'a']<size[s2[i]-'a']){//存在且还有可用空间 len++; size_tmp[s2[i]-'a']++; } else if(size_tmp[s2[i]-'a']==size[s2[i]-'a']){//没有可用空间了,说明重复了子串一个字符 /**当前字符是s1中已经访问过的字符,则往前重新访问记录到无法继续访问**/ len=0; memset(size_tmp,0,sizeof(int)*26); int z; for(int z=i;z>=0;z--){ if(mp.find(s2[z])==mp.end()) break;//不是子串内容退出 if(size_tmp[s2[z]-'a']<size[s2[z]-'a']){//存在且有容量 len++; size_tmp[s2[z]-'a']++; } else if(size_tmp[s2[z]-'a']==size[s2[z]-'a']) break; //没有容量了 } } if(len==len_s1) return true; } return false; } };
此处可以不用map容器,只是一开始查找想到了map,后来规定状态才想到了数组。