This problem is probably tough but not so common. Let’s not spend too much time. If there’s an elegant idea then I should try then read it. To keep things simple just assume there are only 3 unique chars
I feel there should be some DP solution as a shorter haystack is clearly easier than a longer haystack
====DP idea 2 (efficiency is decent but clarity is outstanding — satisfaction)
At each position i in the haystack string, we keep a Club of …. “Members” each a palindrome subseq ending at i.
Requirement: Every eligible Member must be in the Club.
For the next position after incrementing i, this DP algo builds the new Club using all earlier ClubS. We look at each [4] Member in each earlier Club. All of these Members are unique by construction 🙂 Member is a COW class, having
* an immutable length
* an immutable “seq” array of subscripts representing the subseq — no two Members have identical contents in this->seq. The last array element is always i
* an immutable hashtable (or array) of “unused chars on my left”. Each entry is {unused char -> stack of positions}. Note this stack is naturally sorted.
This local optimization eliminates the expensive backscan.
The Club class is immutable. Conceptually, there’s no reason to modify a Club once constructed.
What do we do when we examine a Member? If a Member can “grow” (rightward or both ways) to use the new char, then this Member clones itself, grows and joins the new Club. The “growth” shall update the hashtable IFF growing both ways.
The new char itself is automatically a (trivial) Member of this new Club. Crucially, this procedure is how a new palindrome subsequence gets created.
At any time, before i++, the latest constructed Club includes all the palindrome subseq ending at i.
Similar invariants hold for earlier Clubs.
This level of complete control is kinda remarkable, thanks to the elegance of this DP algorithm.
That’s a fairly complete solution, but there might exist efficiency improvements.
— pruning the ever-growing Clubs
Each Club is immutable but at every position we create a new Club.
Say Club-5 (for i=5) has 44 Members, Club-6 has 22 Members, Club-7 has 53 Members. At position 8, We need to examine each Club. However, Some of the Members across the Clubs may be hopelessly short. This can be seen when the unseen section has only 2 chars.
If we can prune the Clubs (treating them as mutable) we can hopefully reduce the total computation cost.
For this purpose, we can keep “Groups” of Members. Group-3 are all the known Members of length 3. This feature doesn’t increase run time cost.
We only need two simple variables to start pruning
* R := remaining chars = len(haystack) – i
* LeadingPack.len
LeadingPack.len – R is the pruning criteria. Any pack whose len falls below are hopeless and pruned
Because pruning has the potential to drastically reduce computation, every record breaker is good news, giving us a chance to prune many existing Members.
— An experimental, optimistic technique — The default implementation would examine every known Member but we can be lazier. As we increment i, we will focus on Group-6 i.e. the leading pack of longest Members, all length 6, across all Clubs. At a given position, when there are only 15 chars on the forward scan, One Group-6 Member might keep growing 15 steps. In that case, this Member can safely be declared the winner, without looking at the “other” Groups.
We would need to remember the original IDs of the Group-6
This is an optimistic algo.
In fact, I might pick one length-6 Member. But if this pick stops growing, then I pick another from the leading pack… backtracking? IFF all of Group-6 stop growing, then we look at Group-5.
I feel this may not work so well, since there’s a high chance that some group-1 Member has the best potential. The current length of a Member is not a good predictor of ultimate length.
If this algo works it is again using auxDS, my strength