统计子串的唯一字符题解

思路1(超时):

对于字符串s的子串s[i]...s[j],如何计算得到该子串的非重复字符数呢?这里考虑使用以下两个数组:
lastRepeatIndex[]:用于存储任一下标的字符其前一次出现的下标,初始化为-1
nextRepeatIndex[]:用于存储任一下标的字符其下一次出现的下标,初始化为s.length()

举一个简单的例子:ABA
lastRepeatIndex = {-1,-1,0}
nextRepeatIndex = {2,3,3}

注意这两个数组一定是满足以下条件的,对于任意的下标i,有:
i > lastRepeatIndex [i]
i < nextRepeatIndex [i]

现在针对任意的子串s[i]...s[j],可以通过下面的方式计算不重复字符的数量:
对于i <= k <= j,该子串中出现重复字符的条件为:lastRepeatIndex[k] >= i || nextRepeatIndex[k] <= j ;对于不满足该重复条件的字符,统计答案+1。

代码如下:

public class Solution {
    public int uniqueLetterString(String s) {
        Map<Character, Integer> repeatMap = new HashMap<>();
        int[] lastRepeatIndex = new int[s.length()];  //前一个重复字符下标
        int[] nextRepeatIndex = new int[s.length()];  //后一个重复字符下标

        Arrays.fill(lastRepeatIndex, -1);
//        Arrays.fill(nextRepeatIndex, -1);
        Arrays.fill(nextRepeatIndex, s.length());

        for (int i = 0; i < s.length(); i++) {
            char key = s.charAt(i);
            if (repeatMap.get(key) != null){
                //已经出现过该字符
                lastRepeatIndex[i] = repeatMap.get(key); //上一个重复的字符下标
            }
            repeatMap.put(key, i);
        }
        repeatMap.clear();
        for (int i = s.length()-1; i >=0; i--) {
            char key = s.charAt(i);
            if (repeatMap.get(key) != null){
                //已经出现过该字符
                nextRepeatIndex[i] = repeatMap.get(key); //上一个重复的字符下标
            }
            repeatMap.put(key, i);
        }

       int ans = s.length();  //先把长度为1的计算加入
       for (int i = 0; i < s.length(); i++) {
           for (int j = i+1; j < s.length(); j++) {
               //计算i-j之间的不重复字符长度

               for (int k = i; k <= j; k++) {
                   //该子字符串中有重复字符的判断条件
                   if(!(lastRepeatIndex[k]>=i || nextRepeatIndex[k]<=j)){
                       ans++;
                   }
               }

           }
       }
        return ans;
    }
}

时间复杂度为O( n 3 n^3 n3)

思路2

在思路1中,主要耗时的地方在于每次需要截取一个子串,并对改子串中每个不重复的字符进行统计并累加进ans中。
既然我们已经有了两个数组:lastRepeatIndexnextRepeatIndex

image.png

我们已经知道了对于任意一个字符是s[i],其前一次出现的下标j以及下一次出现的下标k

前一次没有出现时,lastRepeatIndex[i] = -1
下一次没有出现时,nextRepeatIndex[i] = s.length() = n

那么针对这个字符s[i],其对唯一字符的贡献度的计算思路如下:在s[j]...s[k](不包含边界字符)中包含s[i]的子串的数量。计算方式如下:
● 子串起始位置个数:i-j(包含s[i]不包含s[j])
● 子串结束位置个数:k-i(不包含s[k]包含s[i])
因此其贡献度为(i-j)*(k-i)
因为只需要遍历一遍字符串,所以时间复杂度为O(n)

代码如下:

public class Solution {
    public int uniqueLetterString(String s) {
        Map<Character, Integer> repeatMap = new HashMap<>();
        int[] lastRepeatIndex = new int[s.length()];  //前一个重复字符下标
        int[] nextRepeatIndex = new int[s.length()];  //后一个重复字符下标

        Arrays.fill(lastRepeatIndex, -1);
//        Arrays.fill(nextRepeatIndex, -1);
        Arrays.fill(nextRepeatIndex, s.length());

        for (int i = 0; i < s.length(); i++) {
            char key = s.charAt(i);
            if (repeatMap.get(key) != null){
                //已经出现过该字符
                lastRepeatIndex[i] = repeatMap.get(key); //上一个重复的字符下标
            }
            repeatMap.put(key, i);
        }
        repeatMap.clear();
        for (int i = s.length()-1; i >=0; i--) {
            char key = s.charAt(i);
            if (repeatMap.get(key) != null){
                //已经出现过该字符
                nextRepeatIndex[i] = repeatMap.get(key); //上一个重复的字符下标
            }
            repeatMap.put(key, i);
        }

        int ans = 0;
        for (int i = 0; i < s.length(); i++) {
            ans += (i-lastRepeatIndex[i])*(nextRepeatIndex[i]-i);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值