|
| 1 | +#include <cmath> |
| 2 | +#include <cstdio> |
| 3 | +#include <vector> |
| 4 | +#include <iostream> |
| 5 | +#include <algorithm> |
| 6 | + |
| 7 | +using namespace std; |
| 8 | + |
| 9 | +// Source: https://www.hackerrank.com/contests/world-codesprint-6/challenges/abbr |
| 10 | + |
| 11 | +/** |
| 12 | + * Abbreviation |
| 13 | + * Interesting problem. |
| 14 | + * This problem apparently has a widely |
| 15 | + * accepted O(n^2) dynamic programming answer however |
| 16 | + * I did not naturally come up with that solution. Mine |
| 17 | + * was a regular O(n^2) solution with O(1) space complexity. |
| 18 | + * |
| 19 | + * The first realization I made was that since we are given the |
| 20 | + * most freedom with the lowercase letters we should take a look |
| 21 | + * at the requirements that must be met for the uppercase ones. It |
| 22 | + * is clear that we cannot delete or displace any uppercase characters |
| 23 | + * in the first string so it is important that any uppercase characters |
| 24 | + * that do exist in the first string exist so in the relative order they |
| 25 | + * appear in the second string. It is also essential to realize that the |
| 26 | + * first string can not have any capital letters that do not appear in the |
| 27 | + * second string because there would be no way to ammend the first string |
| 28 | + * given our constraints, to form the second string. In this case we need to |
| 29 | + * start looking at the first string, and for each character make sure that |
| 30 | + * not only it appears in the second string, but that it appears in the second |
| 31 | + * string after the position of the last found character in the second string. |
| 32 | + * This ensure we catch capital letters that appear k times in the first string |
| 33 | + * but only k-1 times in the second string. This is important because we cannot |
| 34 | + * mutate capital letters. |
| 35 | + * |
| 36 | + * After our first string passes the capitalsRelativeOrder test we then need |
| 37 | + * to see if there are the necessary lowercase characters in between the capitals |
| 38 | + * in proper relative order to make the proper abbreviation. We can do this by searching |
| 39 | + * for every character in the second string to find its position in the first. When searching |
| 40 | + * for some character from second string in the first string we'll either come across an uppercase |
| 41 | + * or lowercase character. If we see an uppercase then surely its relative position has been accounted |
| 42 | + * for, however if we only see a lowercase then it must one we can turn to uppercase to make our abbrev. |
| 43 | + * To make sure that all lowercase characters are in the right place for us to make them work we need to |
| 44 | + * keep track of the position in the first string that we found our last character at, and then only search |
| 45 | + * from there to the end of the string for future character. This ensures that when we find a character in the |
| 46 | + * first string it is actually in the correct relative order as the characters in the second string. |
| 47 | + * |
| 48 | + * The reason we need capitalsRelativeOrder in the first place is because if we just did the searching routine |
| 49 | + * described above, we could come across the following example: |
| 50 | + * |
| 51 | + * s1 = ABBC |
| 52 | + * s2 = ABC |
| 53 | + * In this case there us no way to turn s1 into s2, therefore we need to return false however our algorithm. |
| 54 | + * This why in this algorithm I have two routines. One starting with characters from the first string and searching |
| 55 | + * in the second, and another starting with characters in the second string and finding them in the first. |
| 56 | + * |
| 57 | + * Time complexity: O(n^2) (find operation is O(n) and we call it O(n) times) |
| 58 | + * Space complexity: O(1) |
| 59 | + */ |
| 60 | + |
| 61 | +bool capitalsRelativeOrder(string& s1, string& s2) { |
| 62 | + int pos = 0; |
| 63 | + for (int i = 0; i < s1.length(); ++i) { |
| 64 | + if (s1[i] >= 65 && s1[i] <= 90) { // if (s1[i] >= 'A' && s1[i] <= 'Z') { |
| 65 | + if (s2.find(s1[i], pos) == string::npos) { |
| 66 | + return false; |
| 67 | + } else if (s2.find(s1[i], pos) != string::npos) { |
| 68 | + pos = s2.find(s1[i], pos); |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + return true; |
| 74 | +} |
| 75 | + |
| 76 | +bool canMakeAbbrev(string& s1, string& s2) { |
| 77 | + if (!capitalsRelativeOrder(s1, s2)) return false; |
| 78 | + int pos = 0; |
| 79 | + for (int i = 0; i < s2.length(); ++i) { |
| 80 | + if (s1.find(s2[i], pos) == string::npos && s1.find(char(s2[i] + 32), pos) == string::npos) { |
| 81 | + return false; |
| 82 | + } else if (s1.find(s2[i], pos) != string::npos || s1.find(char(s2[i] + 32), pos) != string::npos) { |
| 83 | + pos = i; |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + return true; |
| 88 | +} |
| 89 | + |
| 90 | +int main() { |
| 91 | + int q; |
| 92 | + string s1, s2; |
| 93 | + cin >> q; |
| 94 | + |
| 95 | + for (int i = 0; i < q; ++i) { |
| 96 | + cin >> s1 >> s2; |
| 97 | + if (canMakeAbbrev(s1, s2)) { |
| 98 | + cout << "YES"; |
| 99 | + } else { |
| 100 | + cout << "NO"; |
| 101 | + } |
| 102 | + cout << endl; |
| 103 | + } |
| 104 | + |
| 105 | + return 0; |
| 106 | +} |
0 commit comments