• 最长回文子串


    问题描述:

    给定一个字符串S,找出它的最长回文子串并返回,最长子串的长度不超过1000.

    Example:

    Input: "babad"
    
    Output: "bab"
    
    Note: "aba" is also a valid answer.
    

    Example:

    Input: "cbbd"
    
    Output: "bb"
    

     方法1(自己想的)

     把子串分成两类,奇子串和偶子串,再分别以这一个字母和两个字母为中心进行扩展,时间复杂度为O(n*2)。

     public String longestPalindrome(String s) {
            int maxLen = 0;
            int a=0,b=0,x=0,y=0;//a和b存放当前子串的下标,x和y存放当前最长回文子串的下标。
    
            for(int i=0;i<s.length();i++) {
                int len =1;
                
                a=i;
                b=i;
                for(int j=1;j<=s.length()/2;j++) {//循环扩展i,并把i当做是奇回文                    
                    if((i-j)>=0 && (i+j)<s.length()) {//判断是否超出了边界
                        if (s.charAt(i-j)==s.charAt(i+j)) {//如果i和i+1相等,则相应增减len和a,b
                            len +=2;
                            a--;
                            b++;
                        }
                        else break;
                    }
                }
                
                if(len>maxLen) {//判断一次最大回文长度
                    x = a;
                    y = b;
                    maxLen = len;
                }
                
                if(i<s.length()-1 && s.charAt(i)==s.charAt(i+1)) {//这里的i和i+1的字母是相等的,把他两当做是偶回文的中心
                    len =2;//len和a,b做相应的改变
                    a = i;
                    b = i+1;
                    for(int j=1;j<=s.length()/2;j++) {//扩展i和i+1
                        if((i-j)>=0 && (i+j+1)<s.length()) {
                            if (s.charAt(i-j)==s.charAt(i+j+1)) {
                                len +=2;
                                a--;
                                b++;
                            }
                            else break;
                        }
                    }
                }            
                
                if(len>maxLen) {//判断最大回文长度
                    x = a;
                    y = b;
                    maxLen = len;
                }
            }
            
            return s.substring(x,y+1);//输出最长回文子串
        }

    方法2(整理中)

     首先,在字符串s中,用rad[i]表示第i个字符的回文半径,即rad[i]尽可能大,且满足:
    s[i-rad[i],i-1]=s[i+1,i+rad[i]]
    很明显,求出了所有的rad,就求出了所有的长度为奇数的回文子串.
    至于偶数的怎么求,最后再讲.
    假设现在求出了rad[1..i-1],现在要求后面的rad值,并且通过前面的操作,得知了当前字符i的rad值至少为j.现在通过试图扩大j来扫描,求出了rad[i].再假设现在有个指针k,从1循环到rad[i],试图通过某些手段来求出[i+1,i+rad[i]]的rad值.
    根据定义,黑色的部分是一个回文子串,两段红色的区间全等.
    因为之前已经求出了rad[i-k],所以直接用它.有3种情况:
    浅谈manacher算法
    ①rad[i]-k<rad[i-k]
    如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分超过了黑色的部分,所以rad[i+k]肯定至少为rad[i]-k,即橙色的部分.那橙色以外的部分就不是了吗?这是肯定的.因为如果橙色以外的部分也是回文的,那么根据青色和红色部分的关系,可以证明黑色部分再往外延伸一点也是一个回文子串,这肯定不可能,因此rad[i+k]=rad[i]-k.为了方便下文,这里的rad[i+k]=rad[i]-k=min(rad[i]-k,rad[i-k]).
    浅谈manacher算法
    ②rad[i]-k>rad[i-k]
    如图,rad[i-k]的范围为青色.因为黑色的部分是回文的,且青色的部分在黑色的部分里面,根据定义,很容易得出:rad[i+k]=rad[i-k].为了方便下文,这里的rad[i+k]=rad[i-k]=min(rad[i]-k,rad[i-k]).

    根据上面两种情况,可以得出结论:当rad[i]-k!=rad[i-k]的时候,rad[i+k]=min(rad[i]-k,rad[i-k]).
    注意:当rad[i]-k==rad[i-k]的时候,就不同了,这是第三种情况:
    浅谈manacher算法
    如图,通过和第一种情况对比之后会发现,因为青色的部分没有超出黑色的部分,所以即使橙色的部分全等,也无法像第一种情况一样引出矛盾,因此橙色的部分是有可能全等的,但是,根据已知的信息,我们不知道橙色的部分是多长,因此就把i指针移到i+k的位置,j=rad[i-k](因为它的rad值至少为rad[i-k]),等下次循环的时候再做了.
    整个算法就这样.
    至于时间复杂度为什么是O(n),我已经证明了,但很难说清楚.所以自己体会吧.

      	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		String s ="acbcdef";
    		char[] c = new char[s.length()*2+1];
    		int[] rad = new int[c.length];
    		rad[0] = 0;
    		rad[1] = 1;
    		int maxLength=0;
    		
    		for(int i=0;i<s.length();i++) {//在字符串插入#字符
    			c[2*i] = '#';
    			c[2*i+1] = s.charAt(i);
    		}
    		c[s.length()*2] = '#';
    		
    		for(int i=1,j=0,k=0;i<c.length;) {
    			while(i+j+1<c.length && i>=j+1 && c[i-j-1]==c[i+j+1]) j++;//扩展i的两边,找到其最大子串
    			
    			rad[i] = j;//将最大子串的值赋给rad
    			for(k=1;i>=k && i+k<c.length && rad[i-k]!=rad[i]-k;k++) rad[i+k] = Math.min(rad[i-k],rad[i]-k);//如果i-k与rad[i]-k的值不等,那么把最小的值赋给radi+k
    			
    			i+=k;//如果i-k与radi-k的值相等,那么进入下一次循环
    			j -= k;//下一个i已经能确定的最小半径
    		}
    		
    		for(int i=0;i<c.length;i++) maxLength = Math.max(maxLength, rad[i]);
    		
    		System.out.println(maxLength);
    		
    	}
    

      

      

  • 相关阅读:
    新闻发布项目——实体类(comment)
    新闻发布项目——实体类(comment)
    新闻发布项目——实体类(newsTb)
    新闻发布项目——实体类(newsTb)
    新闻发布项目——实体类(newsTb)
    新闻发布项目——实体类(User)
    新闻发布项目——实体类(User)
    新闻发布项目——实体类(User)
    2016经典微小说:《轮回》
    要么干,要么滚,千万别混
  • 原文地址:https://www.cnblogs.com/whig/p/8359631.html
Copyright © 2020-2023  润新知