字符串一直以来都是我不擅长处理的题目,解题思路一般两种 BF 和 DP,我一般BF不行了就直接DFS (Backtracking?)。先上几道题目,看看有没有什么共同特征。
题目一:Word Break
先来一发简单的,我都不知道算不算得上是DP了,反正是一维的而且还有记录本啥的。
Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one dictionary words. For example, given s="leetcode", dict=["leet", "code"], return true.
思路:这道题只用返回TRUE or FALSE。最开始我二逼的将字符串一个个单词切分匹配,不仅有递归而且还有大量的substr。。。通过某道题的启发(Jump?)想到一个O(n)的方法而逆袭成功。想想递推式~~据目前的情形看来,在字符串匹配及变形题中用递归貌似都有点二逼。
class Solution {
public:
bool wordBreak(string s, unordered_set<string> &dict) {
int sz=s.length();
if(sz==0) return false;
bool c[sz+1];
for(int i=1;i<sz+1;i++) c[i]=false;
c[0]=true;
for(int i=0;i<sz;i++){
if(c[i]){
unordered_set<string>::iterator it=dict.begin();
for(;it!=dict.end();it++){
string newstr=s.substr(0,i)+*it;
if(s.find(newstr)<s.length())
c[newstr.length()]=true;
}
}
}
return c[sz];
}
};
题目二: Word Break II
Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible senstences. For example, given s="catsanddog", dict=["cat", "cats", "and", "sand", "dog"], a solution is ["cats and dog", "cat sand dog"].
第一道题水水就行了,看到这道题要呆掉了,尼玛貌似之前的方法完全不能用嘛!还是重操旧业——递归,可以枚举出所有的解,这大概是递归最大的优点吧。如果只用递归的话,也会出现Word break中的TLE问题,因为存在很多没有必要的枚举,故放在前面的解法,做一个记录本, 记录范围过的[1..s.size()-1]中访问过的点。
class Solution {
private:
int lmin, lmax;
public:
vector<string> wordBreak(string s, unordered_set<string> &dict) {
vector<string> res;
string words="";
map<int, bool> valid;
lmin=1<<12; lmax=0;
for(unordered_set<string>::iterator it=dict.begin(); it!=dict.end(); it++){
if(it->length()<lmin) lmin=it->length();
if(it->length()>lmax) lmax=it->length();
}
doWordBreak(s, dict, res, words, valid, 0);
return res;
}
bool doWordBreak(string s, unordered_set<string>& dict, vector<string >& res, string words, map<int, bool>& valid, int lev){
if(lev>=s.length()){
res.push_back(words.substr(0, words.length()-1));
return true;
}
if(valid.count(lev) && valid[lev]==false)
return false;
int flag=false;
for(int i=lmin; i<=lmax && i+lev<=s.length(); i++){
string tmp=s.substr(lev,i);
if(dict.count(tmp)){
if(doWordBreak(s, dict, res, words+tmp+" ", valid, lev+i)){
flag=true;
valid[lev+i]=true;
}
else valid[lev+i]=false;
}
}
return flag;
}
};
上面两题都是带字典的break words, 下面这道这是纯的字符串的划分。
Given a string containing only digits, restore it by returning all possible valid IP address combinations. For example: Given "25525511135", return ["255.255,11.135", "255.255.111.35"].
思路:IP地址有自己固有的格式。这导致输入字符串长度必须在[4, 12], 其没三位的数值不超过255. 这样要列出所有的可能解,递归是比较直接的选择,但是这样考虑到字符串格式固定且长度优先,直接3层循环就可以了。
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
vector<string> res;
//异常输入处理!
if (s.size() > 12 || s.size() < 4)
return res;
for(int i=1;i<4;i++){
string first=s.substr(0,i);
if(!isValid(first)) continue;
for(int j=1;i+j<s.size()&&j<4; j++){
string second=s.substr(i,j);
if(!isValid(second)) continue;
for(int k=1;i+j+k<s.size()&&k<4;k++){
string third=s.substr(i+j,k);
string fourth=s.substr(i+j+k);
if(isValid(third) && isValid(fourth)){
string ip=first+"."+second+"."+third+"."+fourth;
res.push_back(ip);
}
}
}
}
return res;
}
bool isValid(string s){
if(s.size()>1 && s[0]=='0') return false; //如果是两位或以上且首位为0,则不合格
if(stoi(s)>=0 && stoi(s)<=255) return true; //高级用法stoi!
else return false;
}
};