#include <bits/stdc++.h> #define ll long long const int MAX_N = 600; const ll MOD = 1e9+7; int DP[MAX_N+3][MAX_N+3]; void calc_dp_prefs(std::string &S) { // DP[i][j] - number of different subsequences with load j on prefix S[1...i] // if S[i-1] == 'L' // DP[i][j] = DP[i-1][j] + DP[i-1][j-1] - DP[prev(i)][j] // if S[i-1] == 'R' // DP[i][j] = DP[i-1][j] + DP[i-1][j+1] - DP[prev(i)][j] // AND HERE WE WANT ONLY NON-NEGATIVE LOADS int n = S.length()-1; std::vector<int> prev(n+1, 0); int last_L, last_P; last_L = last_P = 0; for (int i = 1; i <= n; i++) { if (S[i] == 'L') { prev[i] = last_L; last_L = i; } if (S[i] == 'P') { prev[i] = last_P; last_P = i; } } for (int i = 0; i <= n+1; i++) for (int load = 0; load <= n; load++) DP[i][load] = 0; DP[0][0] = 1; for (int i = 1; i <= n; i++) { for (int load = 0; load <= n; load++) { DP[i][load] = DP[i-1][load]; if (S[i] == 'L' && load > 0) { DP[i][load] += DP[i-1][load-1]; if (prev[i] > 0) DP[i][load] -= DP[prev[i]-1][load-1]; } else if (S[i] == 'P' && load < n) { DP[i][load] += DP[i-1][load+1]; if (prev[i] > 0) DP[i][load] -= DP[prev[i]-1][load+1]; } if (DP[i][load] >= MOD) DP[i][load] -= MOD; else if (DP[i][load] < 0) DP[i][load] += MOD; } } } void calc_dp_sufs(std::string &S) { // same as calc_dp_prefs but we calc it for sufixes of S // BUT WE WANT THEM TO BE ALSO BALANSED IN SOME REVERSED KIND OF WAY // I DON'T KNOW BUT IT LOOKS LIKE ITS WORKING int n = S.length()-1; std::vector<int> next(n+1, 0); int last_L, last_P; last_L = last_P = n+1; for (int i = n; i >= 1; i--) { if (S[i] == 'L') { next[i] = last_L; last_L = i; } if (S[i] == 'P') { next[i] = last_P; last_P = i; } } for (int i = 0; i <= n+1; i++) for (int load = 0; load <= n; load++) DP[i][load] = 0; DP[n+1][0] = 1; for (int i = n; i >= 1; i--) { for (int load = 0; load <= n; load++) { DP[i][load] = DP[i+1][load]; if (S[i] == 'L' && load < n) { DP[i][load] += DP[i+1][load+1]; if (next[i] <= n) DP[i][load] -= DP[next[i]+1][load+1]; } else if (S[i] == 'P' && load > 0) { DP[i][load] += DP[i+1][load-1]; if (next[i] <= n) DP[i][load] -= DP[next[i]+1][load-1]; } if (DP[i][load] >= MOD) DP[i][load] -= MOD; else if (DP[i][load] < 0) DP[i][load] += MOD; } } } int dp2[MAX_N+3][MAX_N+3][2]; void calc_dp_2(std::string &S, int k) { // dp2[k][j][0] - number of different subsequences in s[k] with load j starting at first 'L' // dp2[k][j][1] - ............................................................. at first 'R' calc_dp_sufs(S); int n = S.length()-1; int first_L = 0; for (int i = 1; i <= n; i++) if (S[i] == 'L') { first_L = i; break; } if (first_L > 0) for (int load = 0; load < n; load++) dp2[k][load][0] = DP[first_L+1][load+1]; int first_P = 0; for (int i = 1; i <= n; i++) if (S[i] == 'P') { first_P = i; break; } if (first_P > 0) for (int load = 1; load <= n; load++) dp2[k][load][1] = DP[first_P+1][load-1]; } std::vector<std::string> v; ll calculate_combinations(int i, int j) { // The first part I want to take from what's calculated in DP[][] // The right part i take from dp2[j] int lenl = v[i].length()-1; int lenr = v[j].length()-1; int last_L, last_P; last_L = last_P = 0; for (int k = 1; k <= lenl; k++) { if (v[i][k] == 'L') last_L = k; else last_P = k; } // these are when right part is empty ll combinations = DP[lenl][0]; ll posl, posr; for (int load = 0; load <= std::min(lenl, lenr); load++) { // load is the load of left part // first letter of right part is 'L' // then possible ends of left part are not before last 'L' posl = DP[lenl][load]; if (last_L > 0) posl -= DP[last_L-1][load]; posr = dp2[j][load][0]; combinations += (posl * posr % MOD); // first letter of right part is 'P' posl = DP[lenl][load]; if (last_P > 0) posl -= DP[last_P-1][load]; posr = dp2[j][load][1]; combinations += (posl * posr % MOD); } combinations--; // empty drybbling combinations = ((combinations % MOD) + MOD) % MOD; return combinations; } int main() { std::ios_base::sync_with_stdio(0); std::cin.tie(NULL); int n; std::cin >> n; std::string z; for (int i = 1; i <= n; i++) { std::cin >> z; z = '#' + z; v.push_back(z); } // first calculate dp2 for all strings - O(n^3) time and O(n^2) space for (int i = 0; i < n; i++) calc_dp_2(v[i], i); // now find results for (int i = 0; i < n; i++) { calc_dp_prefs(v[i]); for (int j = 0; j < n; j++) { std::cout << calculate_combinations(i, j) << " "; } std::cout << "\n"; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | #include <bits/stdc++.h> #define ll long long const int MAX_N = 600; const ll MOD = 1e9+7; int DP[MAX_N+3][MAX_N+3]; void calc_dp_prefs(std::string &S) { // DP[i][j] - number of different subsequences with load j on prefix S[1...i] // if S[i-1] == 'L' // DP[i][j] = DP[i-1][j] + DP[i-1][j-1] - DP[prev(i)][j] // if S[i-1] == 'R' // DP[i][j] = DP[i-1][j] + DP[i-1][j+1] - DP[prev(i)][j] // AND HERE WE WANT ONLY NON-NEGATIVE LOADS int n = S.length()-1; std::vector<int> prev(n+1, 0); int last_L, last_P; last_L = last_P = 0; for (int i = 1; i <= n; i++) { if (S[i] == 'L') { prev[i] = last_L; last_L = i; } if (S[i] == 'P') { prev[i] = last_P; last_P = i; } } for (int i = 0; i <= n+1; i++) for (int load = 0; load <= n; load++) DP[i][load] = 0; DP[0][0] = 1; for (int i = 1; i <= n; i++) { for (int load = 0; load <= n; load++) { DP[i][load] = DP[i-1][load]; if (S[i] == 'L' && load > 0) { DP[i][load] += DP[i-1][load-1]; if (prev[i] > 0) DP[i][load] -= DP[prev[i]-1][load-1]; } else if (S[i] == 'P' && load < n) { DP[i][load] += DP[i-1][load+1]; if (prev[i] > 0) DP[i][load] -= DP[prev[i]-1][load+1]; } if (DP[i][load] >= MOD) DP[i][load] -= MOD; else if (DP[i][load] < 0) DP[i][load] += MOD; } } } void calc_dp_sufs(std::string &S) { // same as calc_dp_prefs but we calc it for sufixes of S // BUT WE WANT THEM TO BE ALSO BALANSED IN SOME REVERSED KIND OF WAY // I DON'T KNOW BUT IT LOOKS LIKE ITS WORKING int n = S.length()-1; std::vector<int> next(n+1, 0); int last_L, last_P; last_L = last_P = n+1; for (int i = n; i >= 1; i--) { if (S[i] == 'L') { next[i] = last_L; last_L = i; } if (S[i] == 'P') { next[i] = last_P; last_P = i; } } for (int i = 0; i <= n+1; i++) for (int load = 0; load <= n; load++) DP[i][load] = 0; DP[n+1][0] = 1; for (int i = n; i >= 1; i--) { for (int load = 0; load <= n; load++) { DP[i][load] = DP[i+1][load]; if (S[i] == 'L' && load < n) { DP[i][load] += DP[i+1][load+1]; if (next[i] <= n) DP[i][load] -= DP[next[i]+1][load+1]; } else if (S[i] == 'P' && load > 0) { DP[i][load] += DP[i+1][load-1]; if (next[i] <= n) DP[i][load] -= DP[next[i]+1][load-1]; } if (DP[i][load] >= MOD) DP[i][load] -= MOD; else if (DP[i][load] < 0) DP[i][load] += MOD; } } } int dp2[MAX_N+3][MAX_N+3][2]; void calc_dp_2(std::string &S, int k) { // dp2[k][j][0] - number of different subsequences in s[k] with load j starting at first 'L' // dp2[k][j][1] - ............................................................. at first 'R' calc_dp_sufs(S); int n = S.length()-1; int first_L = 0; for (int i = 1; i <= n; i++) if (S[i] == 'L') { first_L = i; break; } if (first_L > 0) for (int load = 0; load < n; load++) dp2[k][load][0] = DP[first_L+1][load+1]; int first_P = 0; for (int i = 1; i <= n; i++) if (S[i] == 'P') { first_P = i; break; } if (first_P > 0) for (int load = 1; load <= n; load++) dp2[k][load][1] = DP[first_P+1][load-1]; } std::vector<std::string> v; ll calculate_combinations(int i, int j) { // The first part I want to take from what's calculated in DP[][] // The right part i take from dp2[j] int lenl = v[i].length()-1; int lenr = v[j].length()-1; int last_L, last_P; last_L = last_P = 0; for (int k = 1; k <= lenl; k++) { if (v[i][k] == 'L') last_L = k; else last_P = k; } // these are when right part is empty ll combinations = DP[lenl][0]; ll posl, posr; for (int load = 0; load <= std::min(lenl, lenr); load++) { // load is the load of left part // first letter of right part is 'L' // then possible ends of left part are not before last 'L' posl = DP[lenl][load]; if (last_L > 0) posl -= DP[last_L-1][load]; posr = dp2[j][load][0]; combinations += (posl * posr % MOD); // first letter of right part is 'P' posl = DP[lenl][load]; if (last_P > 0) posl -= DP[last_P-1][load]; posr = dp2[j][load][1]; combinations += (posl * posr % MOD); } combinations--; // empty drybbling combinations = ((combinations % MOD) + MOD) % MOD; return combinations; } int main() { std::ios_base::sync_with_stdio(0); std::cin.tie(NULL); int n; std::cin >> n; std::string z; for (int i = 1; i <= n; i++) { std::cin >> z; z = '#' + z; v.push_back(z); } // first calculate dp2 for all strings - O(n^3) time and O(n^2) space for (int i = 0; i < n; i++) calc_dp_2(v[i], i); // now find results for (int i = 0; i < n; i++) { calc_dp_prefs(v[i]); for (int j = 0; j < n; j++) { std::cout << calculate_combinations(i, j) << " "; } std::cout << "\n"; } } |