题目重现:
对一个字符串按照回文进行分割,例如aba|b|bbabb|a|b|aba就是字符串ababbbabbababa的一个回文分割,每一个字串都是一个回文。请找到可以分割的最少的字串数。例如:
ababbbabbababa最少4个字符串,分割三次:a|babbbab|b|ababa
如果字符串整体是回文,则需要0次分割,最少1个字符串
实现思路:
回文子串的查找
该如何更好的判断回文呢?我们设定P[i][j]:
为true时,表示str[i..j]为回文
为false时,表示str[i..j]不是回文
则,当:
i==j时,P[i][j]=true
j==i+1时,P[i][j]=str[i]==str[i]
其他,P[i][j]=P[i+1][j-1]&&(str[i]==str[j])
这个P该如何构建呢?根据其状态转移的方程,P[i][j]所代表的字符串,长度从1开始变化,逐渐到整个字符串,是这样的一个构建的过程,所以外层循环应该是所要判断的字串的长度。基本代码如下:
js代码实现如下:
// 获取所有的子串回文情况
function getP(str){
var len = str.length,
gap = '_';
var p = {};
var i,j,L;
// 只有一个字符的情况是回文
for( i =0;i<len;i++){
p[i+gap+i] = true;
}
// L是i和j之间的间隔数(因为间隔数从小到大渐增,所以大的间隔数总能包含小的间隔数)
for(L=2;L<=len;L++){
// 从0开始
for(i=0;i<=len-L;i++){
j = i+L-1;
if(L === 2){
p[i+gap+j] = (str[i] === str[j]);
}else{
p[i+gap+j] = (str[i]===str[j])&&p[i+1+gap+(j-1)];
}
}
}
return p;
}
我们以符号"_"来分割开始和结束位置,比如从字符串的第二个字符到第5个字符是个子回文,那么上述函数返回的结果中属性'2_5'为TRUE,否则为false.
获取所有的回文分割可行方案
这里的实现方案比较多,比如对所有的回文子串创建树结构,对树进行广度优先遍历,找到最浅的遍历方案的等。我结合了js语言的特性,运用hash来进行处理。当然也可以考虑用数组实现。思路如下:
我们可以获取所有的单个字符开头的回文子串的数组,并组装成一个hash,然后对hash进行遍历,在另一个对象中(或以数组实现也可),找到以当前遍历属性(以"_"分隔)的开始值减一为结尾的所有属性组成数组,再将当前属性链接上去,最终该对象的所有属性值都是实现回文分割的解决方案。最后,我们只需要再次遍历数组,找到所有切割数最少的方案即可。
所有代码
// 获取所有的子串回文情况
function getP(str){
var len = str.length,
gap = '_';
var p = {};
var i,j,L;
// 只有一个字符的情况是回文
for( i =0;i<len;i++){
p[i+gap+i] = true;
}
// L是i和j之间的间隔数(因为间隔数从小到大渐增,所以大的间隔数总能包含小的间隔数)
for(L=2;L<=len;L++){
// 从0开始
for(i=0;i<=len-L;i++){
j = i+L-1;
if(L === 2){
p[i+gap+j] = (str[i] === str[j]);
}else{
p[i+gap+j] = (str[i]===str[j])&&p[i+1+gap+(j-1)];
}
}
}
return p;
}
function getGaps(str){
var len = str.length;
var p = getP(str);
var o = {};
for(var pro in p){
var s = pro.split('_')[0],
val = p[pro];
o[s] = o[s]||[];
val && o[s].push(pro);
}
return o;
}
// 找到hash中所有以lastGap-1结尾的属性数组
function getRelLastPro(lastGap,obj){
lastGap -= 1;
lastGap = lastGap+'';
var ary = [];
for(var pro in obj){
if(pro.lastIndexOf(lastGap) == pro.length-lastGap.length){
ary.push(pro);
}
}
return ary;
}
// 用's'分隔,组装所有可行方案的hash
function getAllList(str){
var gaps = getGaps(str);
var len = str.length;
var allList = {};
for(var gap in gaps){
var ary = gaps[gap];
ary.forEach(function(sae,index){
// 对应gap层的所有属性
var allRelPros = getRelLastPro(gap,allList);
if(allRelPros.length){
allRelPros.forEach(function(curRelPro){
allList[curRelPro+'s'+sae] = 1;
});
}else{
allList[sae] = 1;
}
});
}
return getRelLastPro(len,allList);
}
// 找到所有最小的切割方案
function getMinAry(str){
var ary = getAllList(str);
ary.forEach(function(item,index){
ary[index] = item.split('s');
});
var minLen = Infinity;
var res = [];
ary.forEach(function(item){
var curLen = item.length;
if(curLen == minLen){
res.push(item);
}else if(curLen < minLen){
minLen = curLen;
res = [item];
}
});
return res;
}
console.log(getMinAry('abac'));