You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

最长公共子串算法边界案例排查求助

最长公共子串问题:未考虑的边界案例排查

问题概述

给定两个字符串s和t,找出最长公共子串,输出三个整数:该子串在s中的起始位置、在t中的起始位置(均为0-based)以及长度。例如输入“cool toolbox”,输出“1 1 3”,多解可输出任意一个。

题目详情

输入格式:每行输入包含两个由小写拉丁字母组成的字符串s和t。
约束:所有s的总长度及所有t的总长度均不超过10^5。
输出格式:对每对字符串,输出最长公共子串的起始位置和长度,满足0 ≤ i < |s|,0 ≤ j < |t|,l ≥ 0,且s[i..i+l-1] = t[j..j+l-1],l取最大值。
时间限制:Java 5秒
内存限制:512MB

实现方案

采用二分查找确定最长公共子串长度,结合多项式哈希快速定位匹配子串,代码如下:

import java.util.*;
import java.io.*;
import java.math.BigInteger;

public class common_substring {
    private static int prime1 = 2000000033;
    private static int prime2 = 1000000007;
    private static int multiplier = 97;

    public class Answer {
        int i, j, len;
        Answer(int i, int j, int len) {
            this.i = i;
            this.j = j;
            this.len = len;
        }
    }

    public Answer solveNaive(String s, String t) {
        Answer ans = new Answer(0, 0, 0);
        for (int i = 0; i < s.length(); i++)
            for (int j = 0; j < t.length(); j++)
                for (int len = 0; i + len <= s.length() && j + len <= t.length(); len++)
                    if (len > ans.len && s.substring(i, i + len).equals(t.substring(j, j + len)))
                        ans = new Answer(i, j, len);
        return ans;
    }

    public Answer solve(String s, String t) {
        Answer ans = new Answer(0, 0, 0);

        int minLen = Math.min(s.length(), t.length());
        int shortest = 0, longest = minLen;
        
        // Use binary search to find the length of LONGEST common substring
        while (shortest <= longest) {
            int mid = (shortest + longest) / 2; 
            // In case shortest == 0 and longest == 1, cover the case l = 1
            if (shortest == 0 && longest == 1)
                mid = 1;
            Answer tmp = getSubString(mid, s, t);
            if (tmp.len != 0) {
                shortest = mid + 1;
                ans = tmp;
            }
            else
                longest = mid - 1;
        }

        return ans;
    }

    private Answer getSubString(int l, String s, String t) {
        Answer ans = new Answer(0, 0, 0);

        // Compute the (polynomial) hashes of all substrings of length l
        // First hash
        ArrayList<Long> subHashes1_S = new ArrayList<Long>(s.length() - l + 1);
        ArrayList<Long> subHashes1_T = new ArrayList<Long>(t.length() - l + 1);
        subHashes1_S.add(0, (long) hashFunc1(s.substring(0, l)));
        subHashes1_T.add(0, (long) hashFunc1(t.substring(0, l)));
        // Multiplier for the next substring
        long y1 = (new BigInteger(Integer.toString(multiplier))
            .modPow(new BigInteger(Integer.toString(l)), new BigInteger(Integer.toString(prime1))))
            .longValue(); // (multiplier ^ k) % prime1

        for (int i = 1; i <= s.length() - l; i++) {
            subHashes1_S.add(i, ((subHashes1_S.get(i - 1) * multiplier + s.charAt(i + l - 1) - s.charAt(i - 1) * y1) % prime1 + prime1) % prime1);
        }
        for (int i = 1; i <= t.length() - l; i++) {
            subHashes1_T.add(i, ((subHashes1_T.get(i - 1) * multiplier + t.charAt(i + l - 1) - t.charAt(i - 1) * y1) % prime1 + prime1) % prime1);
        }

        // Second hash
        ArrayList<Long> subHashes2_S = new ArrayList<Long>(s.length() - l + 1);
        ArrayList<Long> subHashes2_T = new ArrayList<Long>(t.length() - l + 1);
        subHashes2_S.add(0, (long) hashFunc2(s.substring(0, l)));
        subHashes2_T.add(0, (long) hashFunc2(t.substring(0, l)));
        // Multiplier for the next substring
        long y2 = (new BigInteger(Integer.toString(multiplier))
            .modPow(new BigInteger(Integer.toString(l)), new BigInteger(Integer.toString(prime2))))
            .longValue(); // (multiplier ^ k) % prime2

        for (int i = 1; i <= s.length() - l; i++) {
            subHashes2_S.add(i, ((subHashes2_S.get(i - 1) * multiplier + s.charAt(i + l - 1) - s.charAt(i - 1) * y2) % prime2 + prime2) % prime2);
        }
        for (int i = 1; i <= t.length() - l; i++) {
            subHashes2_T.add(i, ((subHashes2_T.get(i - 1) * multiplier + t.charAt(i + l - 1) - t.charAt(i - 1) * y2) % prime2 + prime2) % prime2);
        }

        // Compare hashes of two substrings
        // Keys represent the hashes, values represent the indices
        HashMap<Long, Integer> exclusive1 = new HashMap<Long, Integer>(), exclusive2 = new HashMap<Long, Integer>();
        for (int i = 0; i < subHashes1_S.size(); i++) {
            exclusive1.put(subHashes1_S.get(i), i);
            exclusive2.put(subHashes2_S.get(i), i);
        }

        for (int j = 0; j < subHashes1_T.size(); j++) {
            if (exclusive1.containsKey(subHashes1_T.get(j)) && exclusive2.containsKey(subHashes2_T.get(j)))
                ans = new Answer(exclusive1.get(subHashes1_T.get(j)), j, l);
        }
        
        return ans;
    }

    private static int hashFunc1(String s) {
        long hash = 0;
        for (int i = 0; i < s.length(); ++i)
            hash = (hash * multiplier + s.charAt(i)) % prime1;
        return (int)hash;
    }

    private static int hashFunc2(String s) {
        long hash = 0;
        for (int i = 0; i < s.length(); ++i)
            hash = (hash * multiplier + s.charAt(i)) % prime2;
        return (int)hash;
    }

    public void run() {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(System.out);
        in.lines().forEach(line -> {
            StringTokenizer tok = new StringTokenizer(line);
            String s = tok.nextToken();
            String t = tok.nextToken();
            Answer ans = solve(s, t);
            out.format("%d %d %d\n", ans.i, ans.j, ans.len);
        });

        // Stress test
        // boolean again = true;
        // while (again) {
        //     // choose a Character random from this String 
        //     String alphabet = "abcdefghijklmnopqrstuvwxyz";
        //     Random random = new Random();

        //     // create StringBuffer size of AlphaNumericString 
        //     StringBuilder sb1 = new StringBuilder(); 
        //     StringBuilder sb2 = new StringBuilder();

        //     for (int i = 0; i < 120; i++) { 

        //         // add Character one by one in end of sb 
        //         sb1.append(alphabet.charAt(random.nextInt(26)));
        //         sb2.append(alphabet.charAt(random.nextInt(26)));
        //     }

        //     String s = sb1.toString();
        //     String t = sb2.toString();
        //     // s = "yslhjqrnyecavqucgiucgxmn";
        //     // t = "svilryrdllesupkdhkwhbdzh";

        //     Answer ans = solve(s, t);
        //     Answer nai = solveNaive(s, t);
        //     System.out.println(s + " " + t);
        //     System.out.println(String.format("Answer: %d %d %d", ans.i, ans.j, ans.len));
        //     System.out.println(String.format("Naive : %d %d %d", nai.i, nai.j, nai.len));
            
        //     again = ans.len == nai.len;
        // }
        out.close();
    }

    static public void main(String[] args) {
        new common_substring().run();
    }
}

疑问

本地压力测试运行正常,但在线评测持续提示“答案错误”,无法获取测试用例。请问可能存在哪些未考虑到的边界案例?


内容的提问来源于stack exchange,提问作者Andy Nguyen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 20:37:02