题目:
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"]
. (Order does not matter)
代码:
class Solution { public: vector<string> restoreIpAddresses(string s) { const int size = s.size(); vector<string> ret; if ( size>12 || size<4 ) return ret; string tmp; int numDot = 3; Solution::dfs(ret, tmp, s, 0, size-1, numDot); return ret; } static void dfs( vector<string>& ret, string tmp, string& s, int begin, int end, int numDot) { if ( numDot==0 ) { if ( Solution::valid(s.substr(begin,end-begin+1)) ) { tmp += s.substr(begin,end-begin+1); ret.push_back(tmp); } return; } int localEnd = std::min(end, begin+2); for ( int i=begin; i<=localEnd; ++i ) { if ( Solution::valid(s.substr(begin,i-begin+1)) ) { Solution::dfs(ret, tmp+s.substr(begin, i-begin+1)+".", s, i+1, end, numDot-1); } } } static bool valid( string tmp ) { const int len = tmp.size(); if ( len==0 || len>3 || (len>1 && tmp[0]=='0')) return false; int sum = 0; for ( int i = 0; i<len; ++i ) sum = sum*10 + tmp[i]-'0'; return sum<=255; } };
tips:
这道题的基础算法模板还是dfs,但是自己却纠结了好久没有AC。
先不说这道题,之前刷过palindrome partitioning这道题(求一个字符串可能被分割出来的所有回文集合),第一感觉就是跟回文分割的这道题很像,觉得应该很轻松AC。
但很快陷入思维泥潭:
1. 什么时候dfs到下一层?
2. ‘.’这个字符是什么时候加到后面,用不用退出来,什么时候退出来?
最后参考了下面的blog(http://blog.csdn.net/linhuanmars/article/details/24683699)才恍然大悟。
1. 什么时候要dfs到下一层:
a) 需要dfs到下一层的时候呗(对于此题来说,一层就是IP地址中的一段,即两个'.'之间的部分;对于回文分割来说,就是一个回文字串)
b) 敢往下dfs是因为本层的结果是合理的(对于此题来说,合理就是意味着在本层begin到i之间的字符串代表的数字是合法的;对于回文分割来说,本层begin到i构成的字符串,是一个回文)
c) 细化深搜的剪枝条件(对于此题来说,每个IP子端最多有3位数字,且如果长度超过1不能以0开头;对于回文字子符串来说,至少一个元素肯定是回文,再往后走看能否继续是回文,直到走到不能走)
d) 光需要dfs到下一层是不够的,还要看限制条件是否允许dfs到下一层(对于此题来说,IP地址一共有四段,即最多dfs到第四层就必须终结了)
e) 由c)可知dfs的终结条件就是dfs到第四层(对于此题就是numDot==0,numDot初始化为3,每进一层就减1)
f) 对于d)的终止条件,会不会出现begin>end的情况?不会的。因为dfs一层每次增加一个元素,最多加到begin==end,此时经过valid函数判断是无效的,就什么都不做返回上一层,上一层已经到了begin==end的条件→结束,再返回上一层...
2. 如何处理‘.’这个字符:
a) 由于dfs一层代表IP地址的一个段,因此,必须保证进入下一层的时候,tmp的结尾是'.' (想明白这一点比较重要,不会纠结于tmp到底最后一个元素是什么的思维泥潭了)
b) '.'还影响到了终止条件,如果tmp中已经有了三个'.'了(即numDot==0),则下面的已经不需要再分支了,一股脑都加入tmp后面即可(算是一种剪枝策略吧)
完毕~
===================================
第二次过这道题,.011.这种形式的不合法,第一次忘记判断了,后面加入了就AC了。
class Solution { public: vector<string> restoreIpAddresses(string s) { vector<string> ret; if ( s.size()>12 || s.size()<4 ) return ret; vector<string> tmp; Solution::dfs(ret, tmp, s); return ret; } static void dfs(vector<string>& ret, vector<string>& tmp, string s) { if ( tmp.size()==3 ) { if ( Solution::isValid(s) ) { tmp.push_back(s); string str = tmp[0] + "." + tmp[1] + "." + tmp[2] + "." + tmp[3]; ret.push_back(str); tmp.pop_back(); return; } } for ( int i=1; i<=min((int)s.size(),3); ++i ) { if ( !Solution::isValid(s.substr(0,i)) ) continue; tmp.push_back(s.substr(0,i)); Solution::dfs(ret, tmp, s.substr(i,s.size()-i+1)); tmp.pop_back(); } } static bool isValid(string s) { int val = 0; int len = s.size(); if ( len==0 || len>3 || (len>1 && s[0]=='0')) return false; for ( int i=0; i<s.size(); ++i ) val = val*10 + s[i]-'0'; return val<=255; } };