一尘不染

最大产品前缀字符串

algorithm

以下是来自编码面试站点的一个名为codility的演示问题:

字符串S的前缀是S的任何前导连续部分。例如,“ c”和“ cod”是字符串“ codility”的前缀。为简单起见,我们要求前缀为非空。

字符串S的前缀P的乘积是P出现的次数乘以P的长度。更准确地说,如果前缀P由K个字符组成并且P在S中恰好出现了T次,则乘积等于K *T。

例如,S =“ abababa”具有以下前缀:

  • “ a”,其乘积等于1 * 4 = 4,
  • “ ab”,其乘积等于2 * 3 = 6
  • “ aba”,其乘积等于3 * 3 = 9
  • “ abab”,其乘积等于4 * 2 = 8
  • “ ababa”,其乘积等于5 * 2 = 10,
  • “ ababab”,其乘积等于6 * 1 = 6,
  • “ abababa”,其乘积等于7 * 1 = 7。

最长前缀与原始字符串相同。目的是选择这样的前缀以使产品价值最大化。在上面的示例中,最大积为10。

下面是我在Java中需要O(N ^
2)时间的糟糕解决方案。显然有可能在O(N)中执行此操作。我在想Kadanes算法。但是我想不出任何方式可以在每个步骤中对一些信息进行编码,以使我能够找到运行的最大值。有人可以为此考虑O(N)算法吗?

import java.util.HashMap;

class Solution {
    public int solution(String S) {
        int N = S.length();
        if(N<1 || N>300000){
            System.out.println("Invalid length");
            return(-1);
        }
        HashMap<String,Integer> prefixes = new HashMap<String,Integer>();
        for(int i=0; i<N; i++){
            String keystr = "";
            for(int j=i; j>=0; j--) {
                keystr += S.charAt(j);
                if(!prefixes.containsKey(keystr))
                    prefixes.put(keystr,keystr.length());
                else{
                    int newval = prefixes.get(keystr)+keystr.length();
                    if(newval > 1000000000)return 1000000000;
                    prefixes.put(keystr,newval);
                }
            }
        }
        int maax1 = 0;
        for(int val : prefixes.values())
            if(val>maax1)
                maax1 = val;
        return maax1;
    }
}

阅读 289

收藏
2020-07-28

共1个答案

一尘不染

这是基于后缀数组的O(n log n)版本。后缀数组有O(n)个构造算法,我只是没有耐心对它们进行编码。

输出示例(此输出不是O(n),但这只是为了表明我们确实可以计算所有分数):

4*1 a
3*3 aba
2*5 ababa
1*7 abababa
3*2 ab
2*4 abab
1*6 ababab

基本上,您必须反转字符串,并计算后缀数组(SA)和最长公共前缀(LCP)。

然后,您将向后遍历SA数组,以查找与整个后缀(原始字符串中的前缀)匹配的LCP。如果存在匹配项,请增加计数器,否则将其重置为1。每个后缀(前缀)都会收到一个“分数”(SCR),该分数对应于它在原始字符串中出现的次数。

#include <iostream>
#include <cstring>
#include <string>
#define MAX 10050
using namespace std;

int RA[MAX], tempRA[MAX];
int SA[MAX], tempSA[MAX];
int C[MAX];                
int Phi[MAX], PLCP[MAX], LCP[MAX];

int SCR[MAX];

void suffix_sort(int n, int k) {
    memset(C, 0, sizeof C);

    for (int i = 0; i < n; i++)        
        C[i + k < n ? RA[i + k] : 0]++;

    int sum = 0;
    for (int i = 0; i < max(256, n); i++) {                     
        int t = C[i]; 
        C[i] = sum; 
        sum += t;
    }

    for (int i = 0; i < n; i++)        
        tempSA[C[SA[i] + k < n ? RA[SA[i] + k] : 0]++] = SA[i];

    memcpy(SA, tempSA, n*sizeof(int));
}

void suffix_array(string &s) {             
    int n = s.size();

    for (int i = 0; i < n; i++) 
        RA[i] = s[i] - 1;

    for (int i = 0; i < n; i++) 
        SA[i] = i;

    for (int k = 1; k < n; k *= 2) {     
        suffix_sort(n, k);
        suffix_sort(n, 0);

        int r = tempRA[SA[0]] = 0;
        for (int i = 1; i < n; i++) {
            int s1 = SA[i], s2 = SA[i-1];
            bool equal = true;
            equal &= RA[s1] == RA[s2];
            equal &= RA[s1+k] == RA[s2+k];

            tempRA[SA[i]] = equal ? r : ++r;     
        }

        memcpy(RA, tempRA, n*sizeof(int));
    } 
}

void lcp(string &s) {
    int n = s.size();

    Phi[SA[0]] = -1;         
    for (int i = 1; i < n; i++)  
        Phi[SA[i]] = SA[i-1];

    int L = 0;
    for (int i = 0; i < n; i++) {
        if (Phi[i] == -1) { 
            PLCP[i] = 0; 
            continue; 
        }
        while (s[i + L] == s[Phi[i] + L]) 
            L++;

        PLCP[i] = L;
        L = max(L-1, 0);                      
    }

    for (int i = 1; i < n; i++)                 
        LCP[i] = PLCP[SA[i]];
}

void score(string &s) {
    SCR[s.size()-1] = 1;

    int sum = 1;
    for (int i=s.size()-2; i>=0; i--) {
        if (LCP[i+1] < s.size()-SA[i]-1) {
            sum = 1;
        } else {
            sum++; 
        }
        SCR[i] = sum;
    }
}

int main() {
    string s = "abababa";
    s = string(s.rbegin(), s.rend()) +".";

    suffix_array(s);
    lcp(s);
    score(s);

    for(int i=0; i<s.size(); i++) {
        string ns = s.substr(SA[i], s.size()-SA[i]-1);
        ns = string(ns.rbegin(), ns.rend());
        cout << SCR[i] << "*" << ns.size() << " " << ns << endl;
    }
}

我在比赛中使用了很多年的大部分代码(特别是后缀数组和LCP实现)。我改编自几年前写的这个版本。

2020-07-28