查找字符串中的最長迴文串(JAVA實現)

思路寫在代碼裏

判斷一個字符串是否是迴文串

    /***********************
	 * 判斷一個字符串是否是迴文串(左右對稱)  單個數字默認爲是
	 * @param str 需要進行判斷的字符串
	 * @return 返回該整數是否爲迴文串
	 */
    public static boolean isPlalindrome(String str) {
    	//將字符串轉化爲字符數組
        char[] array=str.toCharArray();
        int left=0,right=array.length-1;//記錄數組的開始位置和結束位置
        while(left<right)//開始位置小於結束位置時,進行判斷  兩位置處於對稱位置上
        {
            if(array[left++]!=array[right--])//如果兩位值的字符不相同 則不對稱
                return false;//返回false
        }
        //所有位除奇數位時的中間位均對應 返回true
        return true;
    }

暴力法查找字符串中的最長迴文串

/**************************
 * 求解字符串的最長迴文串----暴力法 O(n^3)
 * 通過兩層循環找出字符串的所有子串  對每一個子串進行判斷 
 * 將是迴文串的子串儲存  當有新的迴文串時,比較記錄中的迴文串和當前迴文串的長度
 * 用較長的串替換當前串  如果兩串長度相同,保留舊的
 * PS:如果想保存所有的迴文串 可以修改記錄迴文串的結構爲String數組(鏈表、hash表都可以)
 * @param str 要求解的字符串
 * @return 返回字符串中的最長迴文串
 */
public static String longestPlalindrome(String original)
{
	//非空判斷
	if((original==null)||original.length()==0)
	{
		return null;
	}
	//將字符串轉換爲字符數組
	char[] oriArray=original.toCharArray();
	int first=0;
	int end=0;//當前字符串中迴文串的始末位置 包含末位置
	
	for(int i=0;i<oriArray.length-1;i++)//兩次循環 查找字符串的所有子串
	{
		for(int j=i;j<oriArray.length;j++)
		{
			//判斷子串是否爲迴文串
			int left=i,right=j;//記錄左側右側的位置
			while(left<right)//左側下標小於右側下標時 比較未完成
			{
				if(oriArray[left]!=oriArray[right])
					break;//如果出現對稱位置不相等元素  則不是迴文串跳出循環
			//判斷下一對稱位置
				left++;
				right--;
				
			}
			if(left>=right)//是否比較完成 是字符串是否爲迴文串的判斷條件
			{
				if(j-i>end-first)//查找到迴文串 且長度大於當前存儲的迴文串長度
				{
					//替換當前迴文串
					first=i;
					end=j;
				}
			}
			/*if(isPlalindrome(oriArray,i,j))//是迴文串
			{
				if(j-i>end-first)//查找到迴文串 
				{
					//替換當前迴文串
					first=i;
					end=j;
				}
			}*/
		}
	}
	//查找結束  將數組轉化爲字符串返回
	return String.valueOf(oriArray, first, end+1);
}

中心拓展法查找字符串中的最長迴文串

/*****************************
 * 求解字符串中的最長迴文串 -----中心拓展法O(n^2)
 * 基本思路  選擇一個字符作爲中心 向兩邊查找回文串
 * 但是查找過程中需要將長度爲奇數的迴文串和長度爲偶數的迴文串分開考慮
 * @param str 要求解的字符串
 * @return 返回字符串中的最長迴文串
 */
public static String longestPlalindromeCenter(String original)
{
	//非空判斷
	if((original==null)||original.length()==0)
	{
		return null;
	}
	//將字符串轉換爲字符數組
	char[] oriArray=original.toCharArray();
	int first=0;
	int end=0;//當前字符串中迴文串的始末位置 包括末位置
	
	//查找奇數位的迴文串
	for(int i=0;i<oriArray.length;i++)
	{
		//i爲中心點  從i開始向兩邊進行判斷
		int preI=i-1,sufI=i+1;//中心點之前和之後的元素
		while((preI>=0)&&(sufI<oriArray.length)&&(oriArray[preI]==oriArray[sufI]))
		{
			//兩側元素相等時 繼續向兩邊進行判斷
			preI--;
			sufI++;
		}
		//當前迴文串的長度大於存儲中的迴文串的長度
		//爲什麼是減2呢?因爲最後preI和sufI下標所在位置的值是不相同的 相同的是preI+1和sufI-1位置的值
		if(sufI-preI-2>end-first)
		{
			first=preI+1;
			end=sufI-1;
		}
	}
	//查找偶數位的迴文串
	for(int i=0;i<oriArray.length-1;i++)//只需要到倒數第二位  因爲偶數位的判斷,需要判斷隨後一位是否與當前位相同
	{
		//相鄰位置不相同
		if(oriArray[i+1]!=oriArray[i])
		{
			continue;
		}
		else
		{
			//i和i+1爲中心點  從i開始向兩邊進行判斷 
			int preI=i-1,sufI=i+2;
			while((preI>=0)&&(sufI<oriArray.length)&&(oriArray[preI]==oriArray[sufI]))
			{
				//兩側元素相等時 繼續向兩邊進行判斷
				preI--;
				sufI++;
			}
			//當前迴文串的長度大於存儲中的迴文串的長度
			if(sufI-preI-2>end-first)
			{
				first=preI+1;
				end=sufI-1;
			}
		}
	}
	//查找結束  將數組轉化爲字符串返回
	return String.valueOf(oriArray, first, end-first+1);
}

動態規劃法查找字符串中的最長迴文串

/*************************
 * 求解字符串中的最長迴文串-----動態規劃法O(n^2)
 * 實現思路:用一個boolean類型的二維數組isPlalindrome[i][j] 來表示i到j之間的字符串是否迴文
 * 其中 i>=j
 * 動態規劃的初值就是  當i=j時,isPlalindrome[i][j]=true;
 * 動態規劃的推導公式爲  當i=j+1時,isPlalindrome[i][j]=(oriArray[i]==oriArray[j]),相鄰兩元素是否相等
 * 					當i>j+1時,需要判斷i與j之間的子串是否是迴文串,即
 * 							isPlalindrome[i][j]=(oriArray[i]==oriArray[j])&&isPlalindrome[i+1][j-1]
 * PS:狀態矩陣賦值過程必須使用左下三角的形式  否則會產生誤判 使用了未賦值的位置
 * @param str 要求解的字符串
 * @return 返回字符串中的最長迴文串
 */
public static String longestPlalindromeDynamic(String original)
{
	//非空判斷
	if((original==null)||original.length()==0)
	{
		return null;
	}
	//將字符串轉換爲字符數組
	char[] oriArray=original.toCharArray();
	int first=0;
	int end=0;//當前字符串中迴文串的始末位置 包括末位置
	
	boolean[][] isPlalindrome=new boolean[oriArray.length][oriArray.length];
	//動歸過程
	for(int i=0;i<oriArray.length;i++)
	{
		for(int j=0;j<=i;j++)
		{
			/*if(i==j)//同一位置
			{
				isPlalindrome[i][j]=true;
			}
			else if(j-i==1)//相鄰元素
			{
				isPlalindrome[i][j]=(oriArray[i]==oriArray[j]);
			}*///合併爲以下部分 這兩部分都是可直接求解的
			if(i-j<2)
			{
				isPlalindrome[i][j]=(oriArray[i]==oriArray[j]);
			}
			else//不相鄰的元素
			{
				isPlalindrome[i][j]=((oriArray[i]==oriArray[j])&&isPlalindrome[i-1][j+1]);
			}
			//一次動歸過程完成 判斷當前是否爲迴文串 如果是 長度是否大於當前存儲的迴文串
			if(isPlalindrome[i][j]&&(i-j)>end-first)
			{
				first=j;
				end=i;
			}
		}
	}
	//查找結束  將數組轉化爲字符串返回
	return String.valueOf(oriArray, first, end+1);
}

馬拉車法查找字符串中的最長迴文串(Manacher’s Algorithm)

/****************************
 * 求解字符串中的最長迴文串----馬拉車方法(Manacher's Algorithm)  O(n)
 * 實現思路:和中心拓展法比較相似
 * 首先預處理原字符串
 * 在最開始添加特殊符號
 * 然後在字符串的每個字符之間以及開始和末尾添加另一種特殊符號,解決奇數迴文串和偶數迴文串的問題
 * 創建與處理後數組等長的輔助數組   assistArray[i]表示以changeArray[i]爲中心的最長迴文子串的半徑,
 * 求解輔助數組引入mx和id兩個變量 mx是迴文串能延伸到的最右端的位置,id爲能延伸到最右端的位置的那個迴文子串的中心點位置
 * 
 * @param str 要求解的字符串
 * @return 返回字符串中的最長迴文串
 */
public static String longestPlalindromeManacher(String original)
{
	//非空判斷
	if((original==null)||original.length()==0)
	{
		return null;
	}
	//將字符串轉換爲字符數組
	char[] oriArray=original.toCharArray();
	int first=0;
	int end=0;//當前字符串中迴文串的始末位置 包括末位置
	
	//對數組做預處理 
	char[] changedArray=new char[oriArray.length*2+3];//一個開始特殊符號  oriArray.length+1個填充符號 一個結尾符號
	int cIndex=0;//修改後數組的遍歷下標
	changedArray[cIndex++]='$';//此處特殊符號選擇美元符號
	for(int i=0;i<oriArray.length;i++)//爲修改後的數組賦值
	{
		changedArray[cIndex++]='#';//填充特殊符號使用井號
		changedArray[cIndex++]=oriArray[i];
	}
	//最後添加#
	changedArray[cIndex++]='#';//cIndex爲changeArray的長度
	changedArray[cIndex++]='%';
	
	//開始進行查找
	int[] assistArray=new int[cIndex];//定義等長輔助數組
	int mx=0,id=0;//輔助求解
	int resLen=0,resCenter=0;//迴文串長度 中心點位置
	for(int i=1;i<cIndex-1;i++)
	{
		//(╯‵□′)╯︵┻━┻   馬拉車太難了
		//核心部分
		if(mx>i)//當前求解位置在已經能夠達到的位置之內
		{
			//當前位置的半徑爲
			//對稱位置和半徑
			//和 當前距離最右端的距離
			//中小的那一個 
			assistArray[i]=Math.min(assistArray[2*id-i], mx-1);
		}
		else
		{
				assistArray[i]=1;
		}
		//在已有迴文串的基礎上求解一個最大回文串
		while(changedArray[i+assistArray[i]]==changedArray[i-assistArray[i]])
			assistArray[i]=assistArray[i]+1;
		//判斷當前所能到達的最右側的位置
		if(mx<i+assistArray[i])
		{
			mx=i+assistArray[i];//可以到達爲止的下一位置
			id=i;//可以到達最右側位置的中心點  與最大回文串無直接聯繫
		}
		//當前求解的最大回文串和存儲中的最大回文串 與上一部分的判斷並無關聯
		if(resLen<assistArray[i])
		{
			resLen=assistArray[i];
			resCenter=i;
		}
	}
	//在改變後數組的中心位置和半徑長度 轉化爲 原數組中的起始點座標
	first=(resCenter-resLen)/2;
	end=resLen-1;
	//查找結束  將數組轉化爲字符串返回
	return String.valueOf(oriArray, first, end );
}

沒看懂的可以下面提問 ,一天內回覆。。。(╯‵□′)╯︵┻━┻

2019-10-20 補個golang動態規劃

func longestPlaindromeDynamic(str string)string{
	strLength := len(str)

	if 0 == strLength{
		return ""
	}

	// 字符串轉爲字符數組
	charArrays := []rune(str)
	// 初始化二維數組
	isPlaindrome := make([][]bool,strLength)
	for i:=0;i<strLength;i++ {
		isPlaindrome[i] = make([]bool,strLength)
	}
	var first,end int

	// 循環遍歷字符串
	for i:=0;i<strLength;i++ {
		for j:= 0 ;j<=i;j++ {
			if i-j<2 {
				isPlaindrome[i][j] = charArrays[i]==charArrays[j]
			} else {
				isPlaindrome[i][j] = (charArrays[i] == charArrays[j]) && isPlaindrome[i-1][j+1]
			}

			if isPlaindrome[i][j] && (end-first)<(i-j){
				end = i
				first = j
			}
		}
	}

	return string(charArrays[first:end+1])
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章