算法:回文子字符串的个数


问题

  • 给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。
    具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

解决

//1、遍历统计(双指针),时间复杂度O(n^3)
class Solution {
    public int countSubstrings(String s) {
        int low,heigh;
        int count=0;
        for(int i=0;i<s.length();i++){
            low=i;
            for(int j=i;j<s.length();j++){
               heigh=j;
               if(isVaild(s,low,heigh)){
                   count++;
               }
            }
        }
        return count;
    }
    public boolean isVaild(String s,int l,int r){
        while(l<r){
            if(s.charAt(l)!=s.charAt(r))
                {
                    return false;
                }
                l++;                        //记住判断以后要移位
                r--;
        }
        return true;
    }

}

// 2、遍历统计(中心拓展),时间复杂度O(n^2)
class Solution {
    public int countSubstrings(String s) {
        int n = s.length(), ans = 0;
        for (int i = 0; i < 2 * n - 1; ++i) {
            int l = i / 2, r = i / 2 + i % 2;                   //从回文中心开始拓展,分奇偶。一样是遍历所有子串
            while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
                --l;
                ++r;
                ++ans;
            }
        }
        return ans;
    }
}

//3、Manacher算法,时间复杂度O(nlogn)
class Solution {
    public int countSubstrings(String s) {
                                                 //处理字符串
        int n = s.length();
        StringBuffer t = new StringBuffer("$#");
        for (int i = 0; i < n; ++i) {
            t.append(s.charAt(i));
            t.append('#');
        }
        n = t.length();
        t.append('!');
                                                //创建对应的最大回文半径数组
        int[] f = new int[n];
        int iMax = 0, rMax = 0, ans = 0;        //回文中心,回文半径,子回文数

        for (int i = 1; i < n; ++i) {       //每个回文中心的回文半径
            // 初始化 f[i],要么是1,要么是
            f[i] = i <= rMax ? Math.min(rMax - i + 1, f[2 * iMax - i]) : 1;
            // 中心拓展
            while (t.charAt(i + f[i]) == t.charAt(i - f[i])) {
                ++f[i];
            }
            // 动态维护 iMax 和 rMax
            if (i + f[i] - 1 > rMax) {
                iMax = i;
                rMax = i + f[i] - 1;
            }
            // 统计答案, 当前贡献为 (f[i] - 1) / 2 上取整
            ans += f[i] / 2;
        }

        return ans;
    }
}
//4、Manacher算法修整版
class Solution{
    public static char[] manacherString(String str) {
		char[] charArr = str.toCharArray();
		char[] res = new char[str.length() * 2 + 1];
		int index = 0;
		for (int i = 0; i != res.length; i++) {
			res[i] = (i & 1) == 0 ? '#' : charArr[index++];		//让字符串中的字符于#交替存储
		}
		return res;							//返回处理好的字符数组
	}

	public static int countSubstrings(String str) {
		if (str == null || str.length() == 0) {
			return 0;
		}
		char[] charArr = manacherString(str);  //项原始字符串添加虚拟(辅助)字符'#'

		int[] pArr = new int[charArr.length];	//用来存储charArr每一位对应的最大回文长度
		int C = -1;			       //回文中心
		int R = -1;				//最大回文半径
                int sum=0;


		for (int i = 0; i != charArr.length; i++) {					//以数组中每个字符为中心

			pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;			//当i被R包主时,pArr的取值

			while (i + pArr[i] < charArr.length && i - pArr[i] > -1) {		//当前位置+/-最大半径数都在charArr数组长度里
				if (charArr[i + pArr[i]] == charArr[i - pArr[i]])		//暴力扩最大半径
					pArr[i]++;
				else {
					break;
				}
			}
			if (i + pArr[i] > R) {			//如果i的最大半径大于R
				R = i + pArr[i];		//将R扩到当前位置+最大半径
				C = i;				//C也移到i位置
			}
			sum+=pArr[i]>>1;                            //使用位运算更快
		}
		return sum;
	}
}

原创文章,作者:,如若转载,请注明出处:https://blog.ytso.com/278528.html

(0)
上一篇 2022年8月3日
下一篇 2022年8月3日

相关推荐

发表回复

登录后才能评论