所有后缀的最长前缀字符串长度

Longest prefix string length for all the suffixes

本文关键字:前缀 字符串 后缀      更新时间:2023-10-16

为字符串的所有后缀查找最长前缀字符串的长度。

例如,字符串ababaa的后缀为ababaababaaabaabaaaaa。这些字符串中的每一个与字符串"ababaa"的相似性分别为6,0,3,0,1,1。因此,答案是6+0+3+0+1+1=11。

我写了以下代码

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <time.h>
int main ( int argc, char **argv) {
    size_t T;
    std::cin >> T;
    char input[100000];
    for ( register size_t i = 0; i < T; ++i) {
        std::cin >> input;
        double t = clock();
        size_t len    = strlen(input);
        char *left    = input;
        char *right   = input + len - 1;
        long long sol = 0;
        int end_count = 1;
        while ( left < right ) {
            if ( *right != '') {
                if ( *left++ == *right++ ) {
                    sol++;
                    continue;
                }
            }
            end_count++;
            left = input; // reset the left pointer
            right = input + len - end_count; // set right to one left.
        }
        std::cout << sol + len << std::endl;
        printf("time= %.3fsn", (clock() - t) / (double)(CLOCKS_PER_SEC));
    }
}

工作正常,但对于一个长为100000且具有相同字符(即aaaaaaaaaa.......a)的字符串,它需要很长时间,我如何才能进一步优化它。

您可以使用后缀数组:http://en.wikipedia.org/wiki/Suffix_array

假设您的ababaa是一个模式p。我认为你可以使用以下算法:

  1. 为P的所有可能后缀创建一个后缀自动机
  2. 使用P作为输入遍历自动机,计算到目前为止遍历的边。对于自动机的每个可接受状态,将当前边缘计数添加到总和中。遍历自动机,直到到达输入的末尾,或者没有更多的边可以通过
  3. 总和就是结果

使用Z算法计算所有子字符串的长度,这些子字符串也在O(n)中加前缀,然后扫描得到的数组并对其值求和。

参考:https://www.geeksforgeeks.org/sum-of-similarities-of-string-with-all-of-its-suffixes/

据我所见,您正在使用纯数组来计算后缀,尽管它可能对某些数据集有效,但在某些情况下却无法有效,例如您提到的情况。

您需要实现前缀树或类似Trie的数据结构。这些代码并不简单,所以如果你不熟悉它们,我建议你读一点关于它们的知识。

我不确定Trie是否会给您带来很大的性能提升。。但我肯定会考虑的。

我的另一个想法是尝试压缩你的字符串。我真的没想过,只是一个疯狂的想法。。。

如果您有这样一个字符串:ababaa,则可能将其压缩为:abab2a。然后你必须想出一种技术,你可以用你的算法处理这些字符串。这样做的好处是可以有效地比较长字符串100000a。或者更重要的是:你可以很快地计算出你的总和。

但是,我没有仔细考虑,也许这是一个非常糟糕的主意;)

这里是一个java实现:

        // sprefix
        String s = "abababa";
        Vector<Integer>[] v = new Vector[s.length()];
        int sPrefix = s.length();
        v[0] = new Vector<Integer>();
        v[0].add(new Integer(0));
        for(int j = 1; j < s.length(); j++)
        {
            v[j] = new Vector<Integer>();
            v[j].add(new Integer(0));
            for(int k = 0; k < v[j - 1].size(); k++)
                if(s.charAt(j) == s.charAt(v[j - 1].get(k)))
                {
                    v[j].add(v[j - 1].get(k) + 1);
                    v[j - 1].set(k, 0);
                }
        }
        for(int j = 0; j < v.length; j++)
            for(int k = 0; k < v[j].size(); k++)
                sPrefix += v[j].get(k);
        System.out.println("Result = " + sPrefix);