• + 0 comments

    Can be done in O(n) using Ukkonen's algorithm (implementation not provided but you can look it up).

    tree.Append(c) = append character c to the search space. searcher.Append(c) = tries to append character c to the search. searcher.Truncate() = removes the first character from the search.

    The suffix links allow to Truncate the search in O(1) and the online property of this algorithm/suffix tree allows the string to be built step by step so we don't have to deal with matching a substring that is not yet fully available.

    The main goal is to construct as much as possible of the string using as little search space as possible. When it is no longer possible to extend the search with the upcoming character in the string, we extend the search space by 1 character, remove the first character from the search (because it is now part of the search space) and try to match the upcoming character again. Repeat until the upcoming character can be matched or the search space can no longer be extended (i.e. when it would include the upcoming character).

    private static int Solve(int a, int b, string s) {
        var dp = new int[s.Length + 1];
        var available = 0;
        var tree = new Ukkonen();
        var searcher = tree.Search();
    
        for (var i = 0; i < s.Length; ++i) {
            bool notFound;
    
            while ((notFound = !searcher.Append(s[i])) && available < i) {
                tree.Append(s[available++]);
                searcher.Truncate();
            }
    
            dp[i + 1] = Math.Min(dp[i] + a, dp[available] + b);
    
            if (notFound) {
                tree.Append(s[available++]);
            }
        }
    
        return dp[s.Length];
    }