【问题标题】:Trie Data Structure in Finding an Optimal Solution在寻找最佳解决方案中尝试数据结构
【发布时间】:2015-08-26 06:23:27
【问题描述】:

这个问题是正在进行的竞赛的一部分,我已经解决了这个问题数据集的 75%,但 25% 给了我 TLE。我在问为什么它给 TLE 我确定我的复杂性是 O(n*n)

问题:

由 N 个小写英文字母组成的字符串 S。我们准备了一个由all non empty substrings of the string S组成的列表L。

现在他问你Q问题。对于第 i 个问题,您需要计算从列表 L 中准确选择 Ki 相等字符串的方法的数量

例如:

    String  = ababa
L = {"a", "b", "a", "b", "a", "ab", "ba", "ab", "ba", "aba", "bab", "aba", "abab", "baba", "ababa"}.
k1 = 2: There are seven ways to choose two equal strings ("a", "a"), ("a", "a"), ("a", "a"), ("b", "b"), ("ab", "ab"), ("ba", "ba"), ("aba", "aba").
k2 = 1: We can choose any string from L (15 ways).
k3 = 3: There is one way to choose three equal strings - ("a", "a", "a").
k4 = 4: There are no four equal strings in L .

Question LINK


我的方法

我正在对 IT 进行 TRIE 并 计算和数组 F[i],其中 F[i] 表示 i 等于字符串出现的次数。 我的 TRIE:

 static class Batman{

        int value;
        Batman[] next = new Batman[26];

        public Batman(int value){
            this.value = value;
            } 
 }

我的插入功能

 public static void  Insert(String S,int[] F , int start){

     Batman temp = Root;
     for(int i=start;i<S.length();i++){
         int index = S.charAt(i)-'a';

         if(temp.next[index]==null){
             temp.next[index] = new Batman(1);
             F[1]+=1;

         }else{
             temp.next[index].value+=1;
             int xx = temp.next[index].value;
             F[xx-1]-=1;
             F[xx]+=1;

            // Calculating The Frequency of I equal Strings
         }
         temp = temp.next[index];
     }

 }

我的主要功能

public static void main(String args[] ) throws  java.lang.Exception  {

Root = new Batman(0);
int n = in.nextInt();
int Q = in.nextInt();
String S = in.next();
int[] F = new int[n+1];

for(int i=0;i<n;i++)
    Insert(S,F,i);


long[] ans = new long[n+1];


for(int i=1;i<=n;i++){
    for(int j=i;j<=n;j++){
        ans[i]+= F[j]*C[j][i];  // C[n][k] is the Binomial Coffecient
        ans[i]%=mod;
    }
}


 while(Q>0){
     Q--;
    int cc = in.nextInt();
    long o =0;
    if(cc<=n) o=ans[cc];
     System.out.println(o+" "+S.length());
 }
}



为什么我的方法是给 TLE,因为时间复杂度是 O(N*N),而字符串的长度是 NWorking CODE

【问题讨论】:

  • 请记住10n*n1000000n*n 都是O(n*n)
  • @azurefrog 我听不懂,请解释一下
  • 在考虑"Big O" notation 时,尤其是当您尝试calculate it for your program 时,您需要记住,当n 很小时(如您的示例中),二次项将主导总时间比 n 大时要少得多。仅仅因为你的程序是 O(n*n) 并不意味着它会运行得很快,只是它的运行时间的缩放会随着输入大小的平方而变化。

标签: java algorithm data-structures tree trie


【解决方案1】:

这个程序获得 TLE 的一个原因(记住时间限制是 1 秒):

每次创建Batman对象时,都会创建一个长度为[26]的数组,相当于添加一个n = 26的循环。

因此,您的时间复杂度为 26*5000*5000 = 650000000 = 6.5*10^8 次操作,理论上,如果 CPU 速度为每秒 10^9 次操作,它仍然可以满足时间限制,但请记住,之后有一些繁重的计算,所以,应该是这个原因。

为了解决这个问题,我使用了Z-algorithm 并被接受:Link

实际代码相当复杂,所以想法是,你有一个表count[i][j],它是匹配子串(i,j)的子串的数量。使用 Z 算法,可以得到 O(n^2) 的时间复杂度。

对于每个字符串s

        int n = in.nextInt();
        int q = in.nextInt();
        String s = in.next();
        int[][] cur = new int[n][];
        int[][] count = new int[n][n];
        int[] length = new int[n];
        for (int i = 0; i < n; i++) {
            cur[i] = Z(s.substring(i).toCharArray());//Applying Z algorithm
            for (int j = 1; j < cur[i].length; j++) {
                if (cur[i][j] > length[j + i]) {
                    for (int k = i + length[j + i]; k < i + cur[i][j]; k++) {
                        count[i][k]++;
                    }
                    length[j + i] = cur[i][j];
                }

            }
        }
        int[] F = new int[n + 1];
        for(int i = 0; i < n; i++){
            for(int j = i; j < n; j++){
                int v = count[i][j] + (length[i] < (j - i + 1) ? 1 : 0);
                F[v]++;
            }
        }

Z-算法方法:

public static int[] Z(char[] s) {
    int[] z = new int[s.length];
    int n = s.length;
    int L = 0, R = 0;
    for (int i = 1; i < n; i++) {
        if (i > R) {
            L = R = i;
            while (R < n && s[R - L] == s[R])
                R++;

            z[i] = R - L;

            R--;
        } else {
            int k = i - L;
            if (z[k] < R - i + 1) {
                z[i] = z[k];
            } else {
                L = i;
                while (R < n && s[R - L] == s[R])
                    R++;
                z[i] = R - L;
                R--;
            }
        }
    }
    return z;
}

实际代码:http://ideone.com/5GYWeS

说明

首先,我们有一个数组长度,length[i] 是与从索引i 开始的字符串匹配的最长子字符串

对于每个索引i,在计算Z函数后,我们看到if cur[i][j] &gt; length[j + i],这意味着存在一个比索引j + i匹配的上一个子字符串长的子字符串,我们没有将它们计入结果中,所以我们需要对它们进行计数。

所以,即使有 3 个嵌套的 for 循环,但每个子字符串只计算一次,这使得整个时间复杂度为 O(n ^2)

        for (int j = 1; j < cur[i].length; j++) {
            if (cur[i][j] > length[j + i]) {
                for (int k = i + length[j + i]; k < i + cur[i][j]; k++) {
                    count[i][k]++;
                }
                length[j + i] = cur[i][j];
            }          
        }

对于下面的循环,我们注意到,如果有匹配的子串(i,j),length[i] &gt;= length of substring (i,j),但如果没有匹配,我们需要加1来计数子串(i,j),如这个子字符串是唯一的。

        for(int j = i; j < n; j++){
            int v = count[i][j] + (length[i] < (j - i + 1) ? 1 : 0);
            F[v]++;
        }

【讨论】:

  • 如果你看一下我的 Insert 函数,它是 O(n),我在我的主要函数中称它为 n 次,所以总体而言 O(n*n)
  • @user4996457 你是对的,刚刚更新了我的答案
  • 那么应该遵循哪种数据结构或方法
  • 我不知道 Z 算法,如果我发现一些问题,我会学习它并锁定你,但是我在本地机器上的 Trie 解决方案在大型数据集上运行良好
  • @user4996457 这个问题的时间限制非常紧迫:)
猜你喜欢
  • 2014-09-18
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 2022-11-11
  • 2012-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多