二进制搜索以找到最长的公共前缀

时间:2017-12-16 19:56:20

标签: java algorithm suffix-array suffix

对于学校作业,我们正在实施后缀阵列,使用构建它的方法并找到最长的公共前缀。我设法很容易地构建和排序后缀数组,但是很难与LCP斗争。

我试图使用一个单一二进制搜索在另一个字符串T中找到模式字符串P的最长公共前缀。该算法应返回最长公共前缀开始的索引。

示例:

  • 如果模式字符串P是“racad”而字符串T是“abracadabra”,则最长的公共前缀应为“racad”,从索引2开始。
  • 同样,如果模式字符串是P“rax”,那么最长的公共前缀应该是“ra”,从索引2或9开始。

    我走得很远,但算法没有返回正确的值。这是我的代码:

    public int compareWithSuffix(int i, String pattern) {
         int c = 0;
         int j = 0;
    
        while (j < pattern.length() && c == 0) {
            if (i + j <= text.length()) {
            c = pattern.charAt(0 + j) - text.charAt(i + j);
            } else {
                c = 1;
            }
            j++;
        }
        return c;
    }
    
    public int binarySearch(String pattern) {
        int left = 0;
        int right = text.length() - 1;
        int mid, c = 0;
    
        while (c != 0 && left <= right) {
            mid = left + (right - left) / 2;
            c = compareWithSuffix(mid, pattern);
    
            if (c < 0) {
                right = mid - 1;
            } else if (c > 0) {
                left = mid + 1;
            } else if (c == 0) {
                return mid;
            }
        }
        return left;
    }
    

我用这个main方法运行它:

public static void main(String[] args) {
    String word = "abracadabra";
    String prefix1 = "rax";
    String prefix2 = "racad";
    SuffixArray s = new SuffixArray(word);

    System.out.println("Longest common prefix of: " + "'" + prefix1 + "'" + " in " + "'" + word + "'" + " begins at index: " + s.binarySearch(prefix1));
    System.out.println("Longest common prefix of: " + "'" + prefix2 + "'" + " in " + "'" + word + "'" + " begins at index: " + s.binarySearch(prefix2));
}

输出始终是我使用。

初始化局部变量left的任何值

搜索算法必须进行单一二进制搜索。我曾尝试搜索其他stackoverflow问题和其他网络资源,但没有找到任何有用的信息。

能否在我的代码中看到任何错误的任何人?

3 个答案:

答案 0 :(得分:1)

我没有深入了解是否这是你的代码中唯一的问题,但是这会立即跳出来作为“输出始终是我初始化左边的局部变量的值”的解释:

int mid, c = 0;

while (c != 0 && left <= right) {

您将c设置为零,然后立即检查它是否不等于零。当然,它不等于零,因此循环条件立即为假,因此循环体永远不会运行。因此,您将返回left的初始值。

为什么要检查c并不明显。在循环内c变为零的唯一情况下,您立即返回。所以只需将你的循环保护改为:

while (left <= right) {

(并在循环中移动c的声明)。

您可以通过使用调试器单步调试代码来轻松找到它。我衷心建议学习如何使用它。

答案 1 :(得分:0)

第一点:分析您给出的示例,您似乎对最长的常见前缀不感兴趣,但是在最长的子串中。前缀始终以单词的第一个字母 - https://en.wikipedia.org/wiki/Prefix

开头

第二点:您是否有兴趣找到设置字词的最长公共子字符串,或只是两个字词?

let url = URL(string: "App-Prefs:root=Bluetooth")

答案 2 :(得分:0)

我在这里提供了一个不同的答案,因为这种方法完全不同,并导致了一个通用的解决方案。 (找到整个字符串列表的公共子字符串)

对于每个单词,我构建所有可能的子串。子字符串由其开始和结束索引确定。如果单词的长度为L,则起始索引可以是:0,1,2,... L-1;如果起始索引为0,则结束索引可以取1到L-1的值,因此L-1值;如果起始索引为1,则结束索引有L-2个可能的值。因此,长度为L的字具有(L-1)+(L-2)+ ... + 1 = L *(L-1)/ 2个子串。这给出了关于L的方形复杂性,但这不是问题,因为单词的长度很少超过15个字母。如果字符串不是单词,而是文本段落,那么我们就会遇到方形复杂度的问题。

接下来,在为每个单词构建子串的集合之后,我构建了这些集合的交集。 主要思想是,更多单词的公共子字符串首先是每个单词中的子字符串,而且,所有这些单词中都会遇到子字符串。这导致为每个单词构建子串集合的想法,然后做交集。

在我们找到所有常见的子串之后,只需迭代并保持最长的

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main4 {

    HashSet<String> allSubstrings(String input)
    {
        HashSet<String> result = new HashSet<String>();
        for(int i=0;i<=input.length()-1;i++)
            for(int j=i+1;j<=input.length();j++)
                result.add(input.substring(i,j));

        return result;
    }

    public HashSet<String> allCommonSubstrings(ArrayList<String> listOfStrings)
    {

        ArrayList<HashSet<String>> listOfSetsOfSubstrings =new ArrayList<HashSet<String>>();
        //for each string in the list, build the set of all its possible substrings
        for(int i=0;i<listOfStrings.size();i++)
        {
            String currentString = listOfStrings.get(i);
            HashSet<String> allSubstrings = allSubstrings(currentString);
            listOfSetsOfSubstrings.add(allSubstrings);
        }

        //find the intersection of all the sets of substrings
        HashSet<String> intersection = new HashSet<String>(listOfSetsOfSubstrings.get(0));
        for(int i=0;i<listOfSetsOfSubstrings.size();i++)
        {
            HashSet<String> currentSet=listOfSetsOfSubstrings.get(i);
            intersection.retainAll(currentSet);
            //retainAll does the set intersection. see: https://stackoverflow.com/questions/8882097/how-to-calculate-the-intersection-of-two-sets

        }

        return intersection;

    }

    public String longestCommonSubstring(HashSet<String> setOfSubstrings)
    {
        if(setOfSubstrings.size()==0)
            return null;//if there are no common substrings, then there is no longest common substrings

        String result="";
        Iterator<String> it = setOfSubstrings.iterator();
        while(it.hasNext())
        {
            String current = it.next();
            if(current.length()>result.length())
                result=current;
        }

        return result;
    }

    public static void main(String[] args)
    {
        Main4 m = new Main4();
        ArrayList<String> list=new ArrayList<String>();
        list.add("bbbaaddd1");
        list.add("bbbaaccc1");
        list.add("dddaaccc1");
        HashSet<String> hset = m.allCommonSubstrings(list);
        Iterator<String> it = hset.iterator();
        System.out.println("all coommon substrings:");
        while(it.hasNext())
        {
            System.out.println(it.next());
        }
        System.out.println("longest common substring:");
        String lcs=m.longestCommonSubstring(hset);
        System.out.println(lcs);
    }
}

输出:

all coommon substrings:
aa
a
1
longest common substring:
aa