数据结构与算法分析笔记与总结(java实现)--字符串12:最长无重复字符子串练习题

题目:对于一个字符串,请设计一个高效算法,找到字符串的最长无重复字符的子串长度。

给定一个字符串A及它的长度n,请返回它的最长无重复字符子串长度。保证A中字符全部为小写英文字符,且长度小于等于500。测试样例:"aabcb",5返回:3

思路:何为无重复子串,所谓无重复子串是指内部没有相同的字符的字符串,在遍历一个字符串时,如果遇到的字符串在之前的字符串中已经存在了那么这个字符串就不再是无重复字符串了。题目要求最终返回无重复子串的最长值。本题技巧性较强,我们知道一个字符串必定有头有尾,在求最长无重复字符串时可以遍历字符串,从头开始计算从该字符开始的最长无重复字符串长度,直到遇到重复的字符为止,逐个字符求从该字符开始的最长长度,逐步保留最长长度,当数组遍历完成时就得到了整个数组中最长无重复字符串的长度。但是这样从头开始求不重复长度每个字符需要再遍历n个字符,时间复杂度是O(n^2)。换种思路,遍历字符串数组,计算以每个字符结尾的字符串中的最长无重复长度,算出他的长度,遍历数组,即计算以每个字符结尾的最长无重复字符串长度,保留最长长度值,当数组遍历完成时就得到了整个数组的最长无重复长度。关键是如何求以每个字符结尾的最长无重复长度。这里利用动态规划的思想,计算某个字符array[i]的最长无重复长度,就是需要得到从该字符array[i]开始向左最远可以到达哪里使得字符串不出现重复,即确定某个位置array[A],在array[A]~ array[i]之间没有重复字符,array[i]再往左就出现了重复字符,于是对于每个字符array[i],关键是确定它的最左边的不重复字符的位置A,而这个位置A是如何确定的呢?A的位置来源有两种可能:对于array[i],array[A]==array[i],即这个元素与array[i]相同,所以显然A不可能再往左移动;或者是array[i-1]最长无重复数组的最左端(理解:要求从array[i]开始向左的最长无重复字符串,不仅要求在这个字符串中不包含与array[i]相同的上一个值,也要求在该数组中不包含与array[i-1]最长无重复字符串相同的字符)。我们在求array[i]向左的最长无重复子序列时,总是已经求得了array[i-1]向左的最长无重复子序列,显然array[i]无重复子序列不应该向左越过array[i-1]的子序列的左端,于是限制array[i]子序列左端的条件有两个,等于array[i]的上一个位置以及array[i-1]的子序列的左端,应该保守的取这两个值中的较大值(靠右的值)作为array[i]的左端点,这就是array[i]结尾子序列的左端点,于是在遍历数组元素时,对于每一个字符都要求出该字符上一次出现的位置A和array[i-1]序列的左端点位置。

对于字符元素array[i],用map[array[i]]求出该字符上一次出现的位置,于是左端应该在map[array[i]]右边,即从map[array[i]]+1开始考虑;用pre表示array[i-1]已经确定的最大无重复子序列的最左端,于是从pre开始进行考虑,比较求出pre=Max(pre,map[array[i]]+1)的较大值作为新的pre,即作为元素array[i]最大无重复子序列的最左端,求出curMaxLength=i-pre+1作为当前元素array[i]的最大无重复子序列的长度。每次遍历到一个元素就要更新哈希表中该字符元素出现的位置,以便下一次使用。由于这种动态规划给出了一种递推的关系,求每一个元素的左端点只需要求出前一个元素的左端点和查询哈希表,因此只需要确定刚开始的第一个元素的左端位置pre就可以一直完成递推求出每个元素的最左端位置(所谓最左端位置不是指上一次出现的位置而是上一次出现位置和前一个元素的左端点的较大者)。对于第一个元素array[0],规定pre=0,哈希表中的值初始化为-1(如果array[i]在之前没有出现过,那么array[i]的最左端不应该由他的上一次出现位置来决定,而是应该完全由前一个元素的最左端来决定,所以应该使得map[array[i]]+1不会影响到Max值的决定,于是规定每个元素的哈希值初始为-1,表示上一次出现在最左端)

注意:本题中每次记录的是某个元素array[i]最长无重复子序列的左端点的下标位置和array[i]上一次出现的下标位置,当前最大长度也是直接通过下标计算得到,都只是操作和改变下标,很方便,不要复杂化。

时间复杂度O(N),空间复杂度O(N)。

importjava.util.*;

importjava.lang.*;

//给定一个字符串,返回其中最大无重复子串的长度

publicclass DistinctSubstring {

    public int longestSubstring(String A, intn) {

       //特殊输入

        if(A==null||A.length()<=0) return 0;

       

       //将字符串转换为数组

        char[] array=A.toCharArray();

       

       //使用哈希表来存放各个元素第一次出现的位置,初始位置为-1

        int[] lastIndexs=new int[256];

       

       //初始化,如果一个元素没有出现过,那么他上一次出现的位置定为-1

        for(inti=0;i

            lastIndexs[i]=-1;

        }

       

       //使用动态规划进行递推之前需要确定开始元素的初始值

        int pre=0;

        int maxLength=1;

       

       //遍历每个元素,求出每个元素的最大无重复子串长度

        for(int i=0;i

            //查询出当前元素的上一次出现的位置

            intlastAppearIndex=lastIndexs[array[i]];

           

   //pre上一个元素对应的最大无重复子串的左端,两者的较大值作为当前元素的最大无重复子串最左端

           pre=Math.max(pre,lastAppearIndex+1);

           

            //本元素的最大无重复子串的长度是curMaxLength

            int curMaxLength=i-pre+1;

           

            //更新当前字符array[i]出现的位置

            lastIndexs[array[i]]=i;

           

            //更新整个字符串中最大的无重复子串的长度

           maxLength=Math.max(maxLength,curMaxLength);

        }

       

        return maxLength;

    }

}

你可能感兴趣的:(数据结构与算法(java实现))