一、引言
字符串可以看作是字符数组。
二、字符串的比较
242、有效字母异位词
给了两个字符串,求他们包含的字符是否完全相同。
class Solution {
public:
vector<int> s_num;
vector<int> t_num;
bool isAnagram(string s, string t) {
//如果每个字符出现的次数相同,则返回true
//使用两个数组,分别记录他们两个的各个字符的个数,再比较这个个数数组/。
s_num = vector<int>(27,0);
t_num = vector<int>(27,0);
getNumofChar(s,t);
//比较两个个数数字是否同
return isSame();
}
void getNumofChar(string s,string t){
for(char c:s){
s_num[c-'a']++;
}
for(char c:t){
t_num[c-'a']++;
}
}
bool isSame(){
for(int i = 0;i<27;i++){
if(s_num[i]!=t_num[i])return false;
}
return true;
}
};
踩坑
- C++前面声明的全局变量中不能直接赋值,赋值应该放到方法中去。
- for中要遍历字符时,可以直接使用string类型,不用转成char*,这是与java不同的。
可以简化为只使用一个数组
class Solution {
public:
vector<int> count;
//可以简化为只用一个数组来实现
bool isAnagram(string s, string t) {
count = vector<int>(26,0);
if(s.length()!=t.length())return false;
getNumofChar(s,t);
//比较两个个数数字是否同
return isSame();
}
void getNumofChar(string s,string t){
for(char c:s){
count[c-'a']++;
}
for(char c:t){
count[c-'a']--;
}
}
bool isSame(){
for(int i = 0;i<26;i++){
if(count[i]!=0)return false;
}
return true;
}
};
205、同构字符串
给了两个字符串,其中s的每一个字符可以一一映射到t的一个字符,则他们同构。
一开始的思路:分别记录两个字符串中用到的每个字符的个数,然后比较这个个数是否是相同的。
class Solution {
vector<int> cnt_s;
vector<int> cnt_t;
public:
bool isIsomorphic(string s, string t) {
if(s.length()!=t.length())return false;
//分别记录两个字符串中各个字符的个数
cnt_s = vector<int>(26,0);
cnt_t = vector<int>(26,0);
count(s,t);
//把个数数组进行从大到小排序
sort(cnt_s.begin(),cnt_s.end());
sort(cnt_t.begin(),cnt_t.end());
//把两个排序数组进行比较
for(int i =0;i<26;i++){
if(cnt_t[i]!=cnt_s[i])return false;
}
return true;
}
void count(string s,string t){
for(int i =0;i<s.length();i++){
cnt_s[s[i]-'a']++;
cnt_t[t[i]-'a']++;
}
}
};
运行时报错了,发现这个情况是不满足同构的,但是按照各个字符的个数来算是没有问题的。因此上述思路不可以解,需要注意每个字符之间的映射关系。
"bbbaaaba"
"aaabbbba"
方法一:使用map进行映射
注意到每一个字符需要映射到另一个字符,那么就把s中的所有字符都映射到t的字符上,但是如果没有出现冲突的话就认为是对的。
但是需要注意可能会出现s中的两个字符映射到t的一个字符上,这样上述方法不会报错,但是不满足题目到一一对应原则,因此需要两个方向的映射。
class Solution {
unordered_map<char,char> maps2t;
unordered_map<char,char> mapt2s;
public:
bool isIsomorphic(string s, string t) {
if(s.length()!=t.length())return false;
for(int i =0;i<s.length();i++){
if(maps2t.count(s[i])==0){
//如果说没找到
maps2t[s[i]] = t[i];
}else{
if(maps2t[s[i]]!=t[i])return false;
}
if(mapt2s.count(t[i])==0){
mapt2s[t[i]] = s[i];
}else{
if(mapt2s[t[i]]!=s[i])return false;
}
}
return true;
}
};
方法二:把它们两个映射到同一个字符串上面。对于s中第一个出现的新的字符记为1,第二个为2,依次类推,并获得一个新的串,t也同样的处理。然后对两个串进行比较。
(比较法语和中文是否相等,可以将他们都转为英语比较)
class Solution {
unordered_map<char,int> maps;
unordered_map<char,int> mapt;
public:
bool isIsomorphic(string s, string t) {
string s1;
string t1;
int sc = 1;
int tc = 1;
if(s.length()!=t.length())return false;
for(int i =0;i<s.length();i++){
if(maps.count(s[i])==0){
//如果说没找到
maps[s[i]] = sc;
sc++;
}else{
s1+=maps[s[i]];
}
if(mapt.count(t[i])==0){
mapt[t[i]] = tc;
tc++;
}else{
t1+=mapt[t[i]];
}
}
return s1==t1;
}
};
647、回文子串
给了一个字符串,求从里面能找到多少个回文子串。
思路:
1、使用DP。
- 考虑到如果说dpij表示从i到j到字符串为回文串,那么它的条件应该是s[i] == s[j],并且应该dp[i+1][j-1]=true。
- 但是有一些特殊情况,比如说i,j之间没有字母,或者i,j之间只有一个值,则不需要判断上述的最后一个条件。此时判断条件应该为s[i] == s[j] + i-j<=2。
class Solution {
public:
int countSubstrings(string s) {
int cnt = 0;
if(s.length()==0)return 0;
vector<vector<bool>> isPa(s.length(),vector<bool>(s.length(),true));
for(int j = 0;j<s.length();j++){
for(int i = 0;i<=j;i++){
if(s[i]==s[j]&&((i>=j-2)||isPa[i+1][j-1]))
cnt++;
else
isPa[i][j] = false;
}
}
return cnt;
}
};
2、以每个位置为起点,判断以它作为中轴的子回文字符串有多少。
- 这个方法巧妙的点在于不是以该位置为起点,而是以该位置为中轴。
- 以某个位置为中轴开始判断的话,则往两边拓展的过程中,一旦出现不满足情况的可以立马终止。
- 但是如果是以某个地方为起点的话,就得判断到最后一位才能结束。
class Solution {
public:
int countSubstrings(string s) {
int cnt = 0;
if(s.length()==0)return 0;
for(int i = 0;i<s.length();i++){
cnt+= getNum(s,i,i);
cnt+=getNum(s,i,i+1);
}
return cnt;
}
int getNum(string s,int p,int q){
int cnt = 0;
while(p>=0&&q<s.length()){
if(s[p]==s[q]){
cnt++;
p--;
q++;
}else
break;
}
return cnt;
}
};
696、计数二进制子串
给了一个二进制串,求它中间的有多少子字符串的0、1个数相同,且字符串中的0要在一起,1要在一起。
方法一、使用刚刚的思路,把每个点当作对称轴的左边,并以此向两边扩展,看当前点作为对称轴能有多少个。以此类推。
class Solution {
public:
int countBinarySubstrings(string s) {
//使用与上一题同样的方法,找以每一个点为中轴左字母的字符串的数量
int cnt = 0;
for(int i =0;i<s.length()-1;i++){
// cout<<"i = "<<i<<endl;
cnt+=getNum(s,i,i+1);
}
return cnt;
}
int getNum(string s,int p,int q){
int cnt = 0;
if(p==(q-1)){
if(s[p]!=s[q]){
cnt++;
p--;
q++;
}
else return 0;
}
while(p>=0&&q<s.length()&&p<q){
if(s[p]!=s[p+1] || s[q]!=s[q-1])break;
cnt++;
p--;
q++;
}
// cout<<"cnt = "<<cnt<<endl;
return cnt;
}
};
遗憾的发现最后十个超时了。
思路2:把它分割成每一段都是同一个字母,求该段的长度,该段与前一段长度的最小值为这两段能形成的复合条件的子串的个数
class Solution {
public:
int countBinarySubstrings(string s) {
int cnt = 0;
int n = s.length();
if(n<=1)return 0;
//由于是连续的0串|1串,因此可以在s中分割出连续的0,1串及其长度,在每次找完本类字符串长度后,可以与上一个串长度比较,找到最小的值,即为他们两个拼凑而成的这个的最小值。
int pre = 0;
int pos = 0;
while(pos<n){
int cur = 0;
char tmp = s[pos];
while(pos<n&&tmp == s[pos]){
cur++;
pos++;
}
cnt += min(cur,pre);
pre = cur;
}
return cnt;
}
};