1
+ package com .problems .dp ;
2
+
3
+ import java .util .Arrays ;
4
+ /*
5
+ * Problem link :
6
+ * https://leetcode.com/problems/longest-palindrome-after-substring-concatenation-i/description/
7
+ * https://leetcode.com/problems/longest-palindrome-after-substring-concatenation-ii/description/
8
+ *
9
+ * Solution link :
10
+ *
11
+ */
12
+
13
+
14
+ public class LongestPalindromeAfterSubstringConcatenation {
15
+ public static void main (String [] args ) {
16
+ type1 ();
17
+ type2 ();
18
+ type3 ();
19
+ }
20
+
21
+ // todo little optimized from the previous approach
22
+ // in the last approach, we were recalculating the length of the palindrome to last from (i) everytime
23
+ // but here we have calculated and stored while we are calculating the longest palindrome
24
+ // now we will use that while we are calculating the longest common substring
25
+ private static void type3 () {
26
+ String s = "axchh" ;
27
+ String t = "semjc" ;
28
+ int result = longestPalindrome3 (s , t );
29
+ System .out .println (result );
30
+ }
31
+
32
+ private static int longestPalindrome3 (String s , String t ) {
33
+ char [] arr1 = s .toCharArray ();
34
+ char [] arr2 = reverse (t .toCharArray ());
35
+
36
+ Object [] result1 = longestPalindromeToLast (arr1 );
37
+ Object [] result2 = longestPalindromeToLast (arr2 );
38
+
39
+ int [] palToLast1Arr = (int []) result1 [1 ];
40
+ int [] palToLast2Arr = (int []) result2 [1 ];
41
+
42
+ int max = Math .max ((int ) result1 [0 ], (int ) result2 [0 ]);
43
+ int n1 = arr1 .length , n2 = arr2 .length ;
44
+ int [][] dp = new int [n1 + 1 ][n2 + 1 ];
45
+ for (int i = 1 ; i <= n1 ; i ++) {
46
+ for (int j = 1 ; j <= n2 ; j ++) {
47
+ if (arr1 [i - 1 ] == arr2 [j - 1 ]) {
48
+ dp [i ][j ] = 1 + dp [i - 1 ][j - 1 ];
49
+ // we will check from string(i+1) what is maximum palindrome can be possible
50
+ int palToLast1 = (i != n1 ) ? palToLast1Arr [i ] : 0 ;
51
+ int palToLast2 = (j != n2 ) ? palToLast2Arr [j ] : 0 ;
52
+ int palToLast = Math .max (palToLast1 , palToLast2 );
53
+ // max(2 * substring length + max palindrome string from the remaining string1 or string2)
54
+ max = Math .max (max , 2 * dp [i ][j ] + palToLast );
55
+ }
56
+ }
57
+ }
58
+ return max ;
59
+ }
60
+
61
+ // it will return the longest palindrome length and the last index of the palindrome
62
+ static Object [] longestPalindromeToLast (char [] arr ) {
63
+ int n = arr .length ;
64
+ // this is dp array to store the max length of the palindrome possible from (i)
65
+ int [] palToLast = new int [n ];
66
+ // all the elements are initialized to 1 as a single length palindrome is possible
67
+ Arrays .fill (palToLast , 1 );
68
+ int max = 1 ;
69
+ for (int i = n - 1 ; i >= 0 ; i --) {
70
+ int len1 = expand (i , i , arr , palToLast );
71
+ int len2 = expand (i , i + 1 , arr , palToLast );
72
+ max = Math .max (max , Math .max (len1 , len2 ));
73
+ }
74
+ return new Object []{max , palToLast };
75
+ }
76
+
77
+ static int expand (int l , int r , char [] arr , int [] palToLast ) {
78
+ int n = arr .length ;
79
+ while (l >= 0 && r < n && arr [l ] == arr [r ]) {
80
+ l --;
81
+ r ++;
82
+ }
83
+ // palindrome is from (l+1) to (r-1), so the length is ((r-1) - (l+1) + 1) => r - l - 1;
84
+ int len = r - l - 1 ;
85
+ // if l is the last element, then we can ignore it,
86
+ // else we will recompute the max length of the palindrome possible from (i)
87
+ if (l != n - 1 )
88
+ palToLast [l + 1 ] = Math .max (palToLast [l + 1 ], len );
89
+ return len ;
90
+ }
91
+
92
+ // todo optimized approach from the previous
93
+ // collecting all the learnings from the previous problems
94
+ // so the concatenation 2 strings need to be palindrome which means if we reverse the 2nd string
95
+ // the problem will become somewhat like (Longest common substring) + (Longest palindrome starting from end1 or end2)
96
+ // so we can create a dp array for both the strings and can find if (i, j) is palindrome or not
97
+ // we will have the dp array for both the strings
98
+ // then we will calculate the longest common substring between the 2 strings
99
+ // once we find the common substring we will check from string(i+1) what is maximum palindrome can be possible
100
+ // similarly for the other string we will compute and take the max
101
+ // so the ans would be max(2 * substring length + max palindrome string from the remaining string1 or string2)
102
+ private static void type2 () {
103
+ String s = "abcde" ;
104
+ String t = "ecdba" ;
105
+ int result = longestPalindrome2 (s , t );
106
+ System .out .println (result );
107
+ }
108
+
109
+ public static int longestPalindrome2 (String s , String t ) {
110
+ char [] arr1 = s .toCharArray ();
111
+ char [] arr2 = reverse (t .toCharArray ());
112
+
113
+ Object [] result1 = longestPalindrome (arr1 );
114
+ Object [] result2 = longestPalindrome (arr2 );
115
+ int [][] pal1 = (int [][]) result1 [1 ];
116
+ int [][] pal2 = (int [][]) result2 [1 ];
117
+
118
+ int max = Math .max ((int ) result1 [0 ], (int ) result2 [0 ]);
119
+ int n1 = arr1 .length , n2 = arr2 .length ;
120
+ int [][] dp = new int [n1 + 1 ][n2 + 1 ];
121
+ for (int i = 1 ; i <= n1 ; i ++) {
122
+ for (int j = 1 ; j <= n2 ; j ++) {
123
+ if (arr1 [i - 1 ] == arr2 [j - 1 ]) {
124
+ dp [i ][j ] = 1 + dp [i - 1 ][j - 1 ];
125
+ // we will check from string(i+1) what is maximum palindrome can be possible
126
+ int palToLast1 = palindromeFromRemainingString (i , pal1 );
127
+ int palToLast2 = palindromeFromRemainingString (j , pal2 );
128
+ int palToLast = Math .max (palToLast1 , palToLast2 );
129
+ // max(2 * substring length + max palindrome string from the remaining string1 or string2)
130
+ max = Math .max (max , 2 * dp [i ][j ] + palToLast );
131
+ }
132
+ }
133
+ }
134
+ return max ;
135
+ }
136
+
137
+
138
+ static int palindromeFromRemainingString (int start , int [][] pal ) {
139
+ int n = pal .length ;
140
+ if (start == n ) return 0 ;
141
+ for (int i = n - 1 ; i > start ; i --) {
142
+ if (pal [start ][i ] != 0 ) return pal [start ][i ];
143
+ }
144
+ return 1 ;
145
+ }
146
+
147
+ static Object [] longestPalindrome (char [] arr ) {
148
+ int n = arr .length ;
149
+ int [][] dp = new int [n ][n ];
150
+ int max = 1 ;
151
+ for (int i = 0 ; i < n ; i ++) dp [i ][i ] = 1 ;
152
+ for (int i = 0 ; i + 1 < n ; i ++) {
153
+ dp [i ][i + 1 ] = (arr [i ] == arr [i + 1 ]) ? 2 : 0 ;
154
+ max = Math .max (max , dp [i ][i + 1 ]);
155
+ }
156
+ for (int d = 3 ; d <= n ; d ++) {
157
+ for (int i = 0 ; i + d <= n ; i ++) {
158
+ int end = i + d - 1 ;
159
+ if (arr [i ] == arr [end ] && dp [i + 1 ][end - 1 ] != 0 ) {
160
+ dp [i ][end ] = 2 + dp [i + 1 ][end - 1 ];
161
+ max = Math .max (max , dp [i ][end ]);
162
+ }
163
+ }
164
+ }
165
+ return new Object []{max , dp };
166
+ }
167
+
168
+
169
+ static char [] reverse (char [] arr ) {
170
+ int n = arr .length ;
171
+ for (int i = 0 , j = n - 1 ; i < j ; i ++, j --) {
172
+ char c1 = arr [i ], c2 = arr [j ];
173
+ arr [i ] = c2 ;
174
+ arr [j ] = c1 ;
175
+ }
176
+ return arr ;
177
+ }
178
+
179
+ // brute force approach
180
+ private static void type1 () {
181
+
182
+ }
183
+ }
0 commit comments