@@ -64,36 +64,77 @@ class PalindromicSubstringsCounter {
64
64
return result . size ;
65
65
}
66
66
67
+ countDPTopDown ( input : string ) {
68
+
69
+ const result : Set < string > = new Set ( ) ;
70
+
71
+ function countHelper ( input : string , beg : number , end : number , dp : boolean [ ] [ ] ) {
72
+
73
+ // Base Conditions where the palindrome is valid
74
+ if ( end < beg ) return true ;
75
+ if ( end == beg ) {
76
+ result . add ( `${ beg } |${ end } ` ) ;
77
+ dp [ beg ] [ end ] = true ;
78
+ return true ;
79
+ }
80
+
81
+ if ( dp [ beg ] [ end ] !== undefined ) {
82
+ return dp [ beg ] [ end ] ;
83
+ }
84
+
85
+ countHelper ( input , beg , end - 1 , dp )
86
+ countHelper ( input , beg + 1 , end , dp )
87
+
88
+ // Case: checking if the strings are palindrome using extremes matching and recursive substring.
89
+ if ( input [ beg ] === input [ end ] && countHelper ( input , beg + 1 , end - 1 , dp ) ) {
90
+ result . add ( `${ beg } |${ end } ` ) ;
91
+ dp [ beg ] [ end ] = true ;
92
+ return true ;
93
+ } else {
94
+ return false ;
95
+ }
96
+ }
97
+
98
+ countHelper ( input , 0 , input . length - 1 , Array . from ( { length : input . length } , ( ) => Array ( input . length ) ) ) ;
99
+ // console.log(result)
100
+
101
+ return result . size ;
102
+ }
103
+
104
+ /**
105
+ * Similar as the recursive brute force solution, but we also use memoization to save the pre-computed results.
106
+ *
107
+ * Time: O(n^2) since the results are computed at least once for each possible pair of beg and end.
108
+ * Space: O(n^2) + O(n) for storing the memoized results and also the recursion depth.
109
+ *
110
+ * @param input
111
+ */
67
112
countDPBottomsUp ( input : string ) {
68
113
69
114
// Create a DP table defining the number of valid palindromic strings for the substrings starting at (row) and ending at (col).
70
- const dp : number [ ] [ ] = Array . from ( { length : input . length + 1 } , ( ) => Array ( input . length ) . fill ( 0 ) ) ;
115
+ const dp : boolean [ ] [ ] = Array . from ( { length : input . length } , ( ) => Array ( input . length ) . fill ( false ) ) ;
71
116
72
117
// Init boundary conditions (for each substring starting and ending at the idx position, there is only 1 valid palindrome.)
73
- for ( let idx = 0 ; idx < input . length ; idx ++ ) dp [ idx ] [ idx ] = 1 ;
74
-
75
- /** a b c d
76
- * a [1 2 3 4
77
- * b 5 6 2]
78
- * c 4 1
79
- */
118
+ for ( let idx = 0 ; idx < input . length ; idx ++ ) dp [ idx ] [ idx ] = true ;
80
119
120
+ let total = input . length ;
121
+
81
122
// Build the solution for all character range. DP row represents the starting char and col represents the ending char.
82
- for ( let beg = input . length ; beg >= 0 ; beg -- ) {
123
+ for ( let beg = input . length - 1 ; beg >= 0 ; beg -- ) {
83
124
for ( let end = beg + 1 ; end < input . length ; end ++ ) {
84
-
85
- // Case 1: If the extremes are matching
86
- // If the substring at beg + 1:end - 1 are matching, we update 1 to the counter.
87
- // If the susbtrings are not matching, we dont have a palindrome, take the last one.
88
- if ( input [ beg ] === input [ end ] ) {
89
- dp [ beg ] [ end ] = dp [ beg + 1 ] [ end - 1 ] ;
125
+ if ( input [ beg ] === input [ end ] && beg + 1 === end ) {
126
+ // If the 2 extremes are equal and there is nothing in between
127
+ dp [ beg ] [ end ] = true
128
+ total ++
129
+ } else if ( input [ beg ] === input [ end ] && dp [ beg + 1 ] [ end - 1 ] ) {
130
+ // If the extremes are matching, we need to ensure the left over substrings are also creating a palindrome
131
+ dp [ beg ] [ end ] = true
132
+ total ++
90
133
}
91
-
92
- dp [ beg ] [ end ] += dp [ beg + 1 ] [ end ] ;
93
134
}
94
135
}
95
136
96
- return dp [ 0 ] [ input . length - 1 ] ;
137
+ return total ;
97
138
}
98
139
99
140
/**
@@ -103,7 +144,7 @@ class PalindromicSubstringsCounter {
103
144
* @param end
104
145
*/
105
146
isValidPalindrome ( input : string , beg : number , end : number ) {
106
- while ( end >= beg ) {
147
+ while ( end >= beg ) {
107
148
if ( input [ end -- ] !== input [ beg ++ ] ) return false ;
108
149
}
109
150
@@ -115,7 +156,8 @@ function testCountPalindromeSubstring(input: string) {
115
156
const counter = new PalindromicSubstringsCounter ( ) ;
116
157
console . log ( `Counter BF for input: ${ input } returned: ${ counter . countBF ( input ) } ` ) ;
117
158
console . log ( `Counter BF Recursive for input: ${ input } returned: ${ counter . countBFRecursive ( input ) } ` )
118
- // console.log(`Counter DP bottoms up for input: ${input} returned for ${counter.countDPBottomsUp(input)}`);
159
+ console . log ( `Counter DP Top down for input: ${ input } returned: ${ counter . countDPTopDown ( input ) } ` )
160
+ console . log ( `Counter DP bottoms up for input: ${ input } returned for ${ counter . countDPBottomsUp ( input ) } ` ) ;
119
161
}
120
162
121
163
testCountPalindromeSubstring ( "abdbca" )
0 commit comments