相邻字符串之间的最长公共前缀

相邻字符串之间的最长公共前缀

第456场周赛

题目

给你一个字符串数组 words,对于范围 [0, words.length - 1] 内的每个下标 i,执行以下步骤:
从 words 数组中移除下标 i 处的元素。
计算修改后的数组中所有 相邻对 之间的 最长公共前缀 的长度。
返回一个数组 answer,其中 answer[i] 是移除下标 i 后,相邻对之间最长公共前缀的长度。如果 不存在 相邻对,或者 不存在 公共前缀,则 answer[i] 应为 0。
字符串的前缀是从字符串的开头开始延伸到任意位置的子字符串。

示例 1:
输入: words = ["jump","run","run","jump","run"]
输出: [3,0,0,3,3]
解释:
移除下标 0:
words 变为 ["run", "run", "jump", "run"]
最长的相邻对是 ["run", "run"],其公共前缀为 "run"(长度为 3)
移除下标 1:
words 变为 ["jump", "run", "jump", "run"]
没有相邻对有公共前缀(长度为 0)
移除下标 2:
words 变为 ["jump", "run", "jump", "run"]
没有相邻对有公共前缀(长度为 0)
移除下标 3:
words 变为 ["jump", "run", "run", "run"]
最长的相邻对是 ["run", "run"],其公共前缀为 "run"(长度为 3)
移除下标 4:
words 变为 ["jump", "run", "run", "jump"]
最长的相邻对是 ["run", "run"],其公共前缀为 "run"(长度为 3)
示例 2:
输入: words = ["dog","racer","car"]
输出: [0,0,0]
解释:
移除任意下标都会导致答案为 0。
提示:
1 <= words.length <= 105
1 <= words[i].length <= 104
words[i] 仅由小写英文字母组成。
words[i] 的长度总和不超过 105。

解答

为了避免重复计算,我们可以先求解每个相邻的公共前缀长度,也就是nextCommon[i]代表i和i+1两个元素的公共前缀长度。顺便,我们可以把i和i+2的公共长度(nnCommon[i])也求了,也就是删除i+1的时候会新增的前缀公共长度。
当移除i时,也就是移除了nextCommon[i]和nextCommon[i-1],新增nnCommon[i-1]。
刚开始想用priorityqueue来处理这种边增变加的情况,后来看了下,priorityqueue不支持对不是队头的元素进行删除。
然后想了下,其实我们只需要知道前几个最大值,然后把“移除”的前缀长和“新增”的前缀长和这几个前几个最大进行比较。如果移除的前缀长是最大值,则answer[i]可能是后面的最大值,或者新增的前缀长。
那我们需要前几个最大值呢?答案是3个,我们移除i时,移除了两个nextCommon,最坏情况时,这两个nextCommon是前两个最大值,那这时候answer[i]可能是这两种中的一个:第三个最大值或者新增的nnCommon[i-1]。

这种解法的时间复杂度是:
O(Len_Sum)+O(N): 先计算nextCommon和nnCommon,这个复杂度是所有words的总长度,然后遍历求ans的复杂度是n数组长度。

import java.util.Arrays;
class Solution {
    public int[] longestCommonPrefix(String[] words) {
        int n=words.length;
        int[] nextCommon=new int[n];
        int[] nnCommon=new int[n];
        for(int i=0;i<n-1;i++){
            nextCommon[i]=calPreLen(words[i],words[i+1]);
            if(i<n-2){
                nnCommon[i]=calPreLen(words[i],words[i+2]);
            }
        }

        // max 3
        int[] max=new int[3];
        int[] maxId=new int[3];
        calMax(nextCommon,max,maxId);

        // System.out.println(Arrays.toString(max));
        
        int[] res=new int[n];
        for(int i=0;i<n;i++){
            //remove i= remove  remove nextCommon[i] & nextCommon[i-1], add nnCommon[i-1]
            res[i]=max[0];
            if(i==maxId[0]||i-1==maxId[0]){
                //delete max0, answer max1 or max2
                if(i==maxId[1]||i-1==maxId[1]){
                    //delete max1, ans=max2
                    res[i]=max[2];
                }else{
                    //ans=max1
                    res[i]=max[1];
                }

            }
            
                //compare res[i] and nnCommon[i-1]
                if(i!=0){
                    if(nnCommon[i-1]>res[i]){
                        // System.out.println("remove i:"+i+" nnC: "+nnCommon[i-1]);
                        res[i]=nnCommon[i-1];
                    }
                }
        }
        return res;
    }

    //max[0] zuida, max[2] dier da
    private void calMax(int[] nCom,int[] max,int[] maxId){
        int n=nCom.length;
        for(int i=0;i<n;i++){
            if(nCom[i]>max[0]){
                max[2]=max[1];
                maxId[2]=maxId[1];
                max[1]=max[0];
                maxId[1]=maxId[0];
                max[0]=nCom[i];
                maxId[0]=i;
            }else if(nCom[i]>max[1]){
                max[2]=max[1];
                maxId[2]=maxId[1];
                 max[1]=nCom[i];
                maxId[1]=i;
            }else if(nCom[i]>max[2]){
                  max[2]=nCom[i];
                maxId[2]=i;
            }
        }
    }

    private int calPreLen(String a,String b){
        char[] aArr=a.toCharArray();
        char[] bArr=b.toCharArray();
        int n1=aArr.length,n2=bArr.length;
        int i=0;
        for(;i<n1&&i<n2;i++){
            if(aArr[i]!=bArr[i]){
                break;
            }
        }

       // System.out.println(a+" len "+b+":"+i);

        return i;
    }
}

大佬code

这里缓存的不是两个相邻元素之间的公共长度,而是[0,i]或者[i,n-1]的相邻公共长度的最大值pre和suf,当删除元素i的时候,直接可以利用pre[i-1]/suf[i+1]的值作为candidate。另外额外求解i左右两个元素的公共长度,从这三个里面选最大的一个。

class Solution {
    public int[] longestCommonPrefix(String[] words) {
        int n = words.length;
        int[] ans = new int[n];
        if (n == 1) {
            return ans;
        }
        int[] pre = new int[n], suf = new int[n];
        for (int i = 1; i < n; i++) {
            int c = 0;
            for (int j = 0; j < Math.min(words[i].length(), words[i - 1].length()); j++) {
                if (words[i].charAt(j) != words[i - 1].charAt(j)) {
                    break;
                }
                c++;
            }
            pre[i] = Math.max(pre[i - 1], c);
        }
        for (int i = n - 2; i >= 0; i--) {
            int c = 0;
            for (int j = 0; j < Math.min(words[i].length(), words[i + 1].length()); j++) {
                if (words[i].charAt(j) != words[i + 1].charAt(j)) {
                    break;
                }
                c++;
            }
            suf[i] = Math.max(suf[i + 1], c);
        }
        ans[0] = suf[1];
        ans[n - 1] = pre[n - 2];
        for (int i = 1; i < n - 1; i++) {
            ans[i] = Math.max(pre[i - 1], suf[i + 1]);
            int c = 0;
            for (int j = 0; j < Math.min(words[i + 1].length(), words[i - 1].length()); j++) {
                if (words[i + 1].charAt(j) != words[i - 1].charAt(j)) {
                    break;
                }
                c++;
            }
            ans[i] = Math.max(ans[i], c);
        }
        return ans;
    }
}
posted @ 2025-06-29 16:43  Fanny123  阅读(247)  评论(0)    收藏  举报