每天一道LeetCode-----将字符串拆分成有效的ip地址
2017-12-19 22:04
417 查看
Restore IP Addresses
原题链接Restore IP Addresses给定一个仅包含数字的字符串,将其拆分成有效的ip地址
题目的主要意思实际是在字符串中添加三个点,从而构成一个ip地址,有效的ip地址格式为
最多包含12个数字
每个数字在[0, 255]之间
数字中除了单一0,不能出现类似0开头的数字,比如192.168.1.013中013是不允许的
所以其实就是随便找到三个位置,判断拆分成的四块数字是否满足要求即可
首先,为了易于理解,在安插”.”的过程中不记录组成的ip地址,只是将”.”的位置记录下来,当”.”的个数为3时统一计算
代码如下
class Solution { public: vector<string> restoreIpAddresses(string s) { vector<string> res; //不合法 if(s.size() > 12) return res; //记录"."的位置 vector<int> dots; dfs(s, res, dots, 0); return res; } private: void dfs(string& s, vector<string>& res, vector<int>& dots, int idx) { if(dots.size() == 3) { //判断四个数字是否符合要求,然后添加 //计算one时的写法是有原因的,可以将dots[-1] 看做 -1 string one = s.substr(-1 + 1, dots[0] - (-1)); string two = s.substr(dots[0] + 1, dots[1] - dots[0]); string three = s.substr(dots[1] + 1, dots[2] - dots[1]); string four = s.substr(dots[2] + 1); if(isValid(one) && isValid(two) && isValid(three) && isValid(four)) res.emplace_back(one + "." + two + "." + three + "." + four); return; } //因为最后一个"."后面必须有数字,所以到s.size() - 1即可 for(int i = idx; i < static_cast<int>(s.size()) - 1; ++i) { //表示将"."放在s[i]的后面 dots.emplace_back(i); dfs(s, res, dots, i + 1); dots.pop_back(); } } bool isValid(string numStr) { if(numStr.size() > 3 || (numStr.size() > 1 && numStr.find_first_not_of('0') != 0) || (numStr.size() == 3 && numStr > "255")) return false; else return true; } };
这种方法比较慢,主要的原因是会将所有可能都找出来然后判断是否合法,也就是说当确定第一个”.”的位置时,这个位置可能是不合适的,但是仍然需要进行到最后
深度优先和回溯法的思想在于将不合法的情况扼杀在摇篮里,也就是要确定”.”的位置时判断是否满足要求,如果不满足要求,就没必要按照这个”.”的位置进行下去
所以,需要在for循环中动手脚,判断”.”的位置是否合适。方法就是判断当前这个”.”和上一个”.”之间的数字是否符合要求,这里用prevIdx变量记录上一个”.”的位置
由上面计算one,two,three,four的公式可知,两个”.”之间的数字正是[prevIdx+1, i],其中
prevIdx记录上一个”.”的位置,初始时为-1,类似公式中的dots[0]
i是当前要确定的”.”的位置,指在s[i]后面插入”.”,类似公式中的dots[1]
有了上面的基础,代码可以更改为
class Solution { public: vector<string> restoreIpAddresses(string s) { vector<string> res; //不合法 if(s.size() > 12) return res; //记录"."的位置 vector<int> dots; dfs(s, res, dots, -1, 0); return res; } private: void dfs(string& s, vector<string>& res, vector<int>& dots, int prevIdx, int idx) { if(dots.size() == 3) { //判断四个数字是否符合要求,然后添加 //计算one时的写法是有原因的,可以将dots[-1] 看做 -1 string one = s.substr(-1 + 1, dots[0] - (-1)); string two = s.substr(dots[0] + 1, dots[1] - dots[0]); string three = s.substr(dots[1] + 1, dots[2] - dots[1]); string four = s.substr(dots[2] + 1); //one two three在确定"."时已经判断过 if(isValid(four)) res.emplace_back(one + "." + two + "." + three + "." + four); return; } //因为最后一个"."后面必须有数字,所以到s.size() - 1即可 for(int i = idx; i < static_cast<int>(s.size()) - 1; ++i) { //判断是否满足要求 if(!isValid(s.substr(prevIdx + 1, i - prevIdx))) return; //表示将"."放在s[i]的后面 dots.emplace_back(i); dfs(s, res, dots, i, i + 1); dots.pop_back(); } } bool isValid(string numStr) { if(numStr.size() > 3 || (numStr.size() > 1 && numStr.find_first_not_of('0') != 0) || (numStr.size() == 3 && numStr > "255")) return false; else return true; } };
再简单一点,可以不需要dots,在遍历的过程中就将最后的ip地址构造好
class Solution { public: vector<string> restoreIpAddresses(string s) { vector<string> res; //不合法 if(s.size() > 12) return res; string cur(""); dfs(s, res, cur, -1, 0, 0); return res; } private: void dfs(string& s, vector<string>& res, string& cur, int prevIdx, int idx, int count) { if(count == 3) { string four = s.substr(idx); if(isValid(four)) res.emplace_back(cur + four); return; } //因为最后一个"."后面必须有数字,所以到s.size() - 1即可 string tmp = cur; for(int i = idx; i < static_cast<int>(s.size()) - 1; ++i) { //判断是否满足要求 if(!isValid(s.substr(prevIdx + 1, i - prevIdx))) break; cur.append(1, s[i]); cur.append(1, '.'); dfs(s, res, cur, i, i + 1, count + 1); //回溯的过程需要回到原来的样子,但是这里只弹出了"."的目的是为了继续扩充当前数字 //不需要回到append(1, s[i])之前的样子,但是return之前需要 cur.pop_back(); } //当返回时回到原来的样子 std::swap(cur, tmp); } bool isValid(string numStr) { if(numStr.size() > 3 || (numStr.size() > 1 && numStr.find_first_not_of('0') != 0) || (numStr.size() == 3 && numStr > "255")) return false; else return true; } };
本题主要思路是在s中选择三个位置作为”.”,同时确定分出的四个数字是否合法
相关文章推荐
- 每天一道LeetCode-----有效回文串
- 每天一道LeetCode-----将字符串切分,使每个子串都是回文串,计算所有可能结果和最小切分次数
- leetcode上做的一道判断括号字符串是否有效的问题
- 每天一道LeetCode-----将字符串切分成若干单词,使得每个单词都在给定的字典中,求出所有的切分结果
- 每天一道LeetCode-----在字符串s中找到最短的包含字符串t中所有字符的子串,子串中字符顺序无要求且可以有其他字符
- 每天一道LeetCode-----只可能有'.'和'*'的字符串正则匹配
- 每天一道LeetCode-----以字符串的形式输出二叉树所有从根节点到叶子节点的路径
- 每天一道LeetCode-----给定字符串s和字符数组words,在s中找到words出现的位置,words内部字符串顺序无要求
- 每天一道LeetCode-----以单词为单位逆序字符串,每个单词之间以一个空格分隔(原字符串中可能有多个空格)
- 每天一道LeetCode-----使用最少的操作将一个字符串转换成另一个字符串,只有插入,删除,替换三种操作
- 每天一道LeetCode-----比较两个字符串,每个字符串被若干'.'分成多个数字,一个个比较
- 每天一道LeetCode-----找到一个字符串在另一个字符串出现的位置,字符串内部顺序无要求
- 每天一道LeetCode-----将用数组表示的整数加一,两个用字符串表示的(二进制)数相加
- 每天一道LeetCode-----将字符串的连续相同的字符合并成一个字符后加个数
- 每天一道LeetCode-----计算字符串s中有多少个子序列和字符串t相等
- 每天一道LeetCode-----寻找给定字符串中重复出现的子串
- 每天一道LeetCode-----判断给定字符串是否符合某个模式
- 每天一道LeetCode-----字符串乘法
- 每天一道算法题(25)——字符串中连续出现次数最多的子串
- 每天一道leetcode