1. 程式人生 > 實用技巧 >[LeetCode 1585] Check If String Is Transformable With Substring Sort Operations

[LeetCode 1585] Check If String Is Transformable With Substring Sort Operations

Given two stringssandt, you want to transform stringsinto stringtusing the followingoperation any number of times:

  • Choose anon-emptysubstring insand sort it in-placeso the characters are inascending order.

For example, applying the operation on the underlined substring in"14234"results in"12344".

Returntrue

ifit is possible to transform stringsinto stringt. Otherwise,returnfalse.

Asubstringis a contiguous sequence of characters within a string.

Example 1:

Input: s = "84532", t = "34852"
Output: true
Explanation: You can transform s into t using the following sort operations:
"84532" (from index 2 to 3) -> "8435
2" "84352" (from index 0 to 2) -> "34852"

Example 2:

Input: s = "34521", t = "23415"
Output: true
Explanation: You can transform s into t using the following sort operations:
"34521" -> "23451"
"23451" -> "23415"

Example 3:

Input: s = "12345", t = "12435"
Output: false

Example 4:

Input: s = "1", t = "2"
Output: false

Constraints:

  • s.length == t.length
  • 1 <= s.length <= 105
  • sandtonly contain digits from'0'to'9'.

Solution 1.

Since we can only sort any substring in ascending order, when s[i] != t[i], if the next available character t[i] in s is at index j(j > i), then it must be true that all the characters in between index i + 1 and j - 1 are bigger than t[i]. Otherwise, it'd be impossible to change s[i] to t[i]. Using this observation, we derive the following greedy algorithm.

1. store all digits' index in order for start string s.

2. keep track of current arrangement count for all digits 0 to 9 for target string t.

3. loop over t from left to right, and do the following checks:

a. for the current character C in t, check if we've already used all available C from s. If so, it means t has more C than s, s and t are not anagram, return false;

b. for characters smaller than C, check if their next available indices are after the next available C's index. If not, it means there is at least one smaller character before the next available C, C can never be swapped to the current t's position, return false.

c. if all checks for each position pass, return true.

Both the runtime and space complexity is O(N).

class Solution {
    public boolean isTransformable(String s, String t) {
        int n = s.length();
        List<Integer>[] sIdx = new List[10];
        for(int i = 0; i < 10; i++) {
            sIdx[i] = new ArrayList<>();
        }
        for(int i = 0; i < n; i++){
            sIdx[s.charAt(i) - '0'].add(i);
        }
        int[] tCnt = new int[10];
        for(int i = 0; i < n; i++) {
            int d = t.charAt(i) - '0';
            if(tCnt[d] >= sIdx[d].size()) {
                return false;
            }
            for(int j = 0; j < d; j++) {
                if(tCnt[j] < sIdx[j].size() && sIdx[j].get(tCnt[j]) < sIdx[d].get(tCnt[d])) {
                    return false;
                }
            }
            tCnt[d]++;
        }
        return true;
    }
}

Solution 2.

There is another elegant O(N) solution. The key idea is that when checking each digit in s from left to right, we map each digit to its correct position in t. At position i, if s[i] is mapped to position j in t, then we know for all positions > i with bigger digits than s[i], the furthest left they can go is j + 1 as they can not go pass left of s[i]'s mapped position j. So we create the mapping of all digits from s to t, then check if each digits in s meet this left bound condition. Each time we confirm a mapping at a position, we also update the bigger digits' left bound constraints.

class Solution {
    public boolean isTransformable(String s, String t) {
        int n = s.length();
        Deque<Integer>[] idx = new ArrayDeque[10];
        //idx[i]: digit i's index in ascending order in string t
        for(int i = 0; i < 10; i++) idx[i] = new ArrayDeque<>();
        for(int i = 0; i < n; i++) {
            idx[t.charAt(i) - '0'].addLast(i);
        }
        //mapTo[i]: mapping from position i in s to s[i]'s correct position in t. 
        int[] mapTo = new int[n];
        for(int i = 0; i < n; i++) {
            int d = s.charAt(i) - '0';
            //if the current digit can not be mapped to t, s and t are not anagrams
            if(idx[d].size() == 0) {
                return false;
            }
            mapTo[i] = idx[d].pollFirst();
        }
        //lBound[i]: the leftMost position(exclusive) that digit i can appear in string t 
        int[] lBound = new int[10];
        for(int i = 0; i < n; i++) {
            //d: current digit in s
            int d = s.charAt(i) - '0';
            //mapTo[i]: current digit's mapped position in t, it can not go to the left of lBound[d]
            if(mapTo[i] < lBound[d]) {
                return false;
            }
            //current digit may further tighten future bigger digits' leftMost bound, they can not go to
            //the left of mapTo[i] as mapTo[i] represents the furthest right position for digit d int t,
            //all future > d digits can not go pass left of this position
            for(int j = d + 1; j < 10; j++) {
                lBound[j] = Math.max(lBound[j], mapTo[i]);
            }
        }
        return true;
    }
}