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

Given two strings s and t, you want to transform string s into string t using the following operation any number of times:

  • Choose a non-empty substring in s and sort it in-place so the characters are in ascending order.

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

Return true if it is possible to transform string s into string t. Otherwise, return false.

A substring is 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) -> "84352"
"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
  • s and t only 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;
    }
}

 

 

 

 

 
 
posted @ 2020-09-15 22:32  Review->Improve  阅读(432)  评论(0编辑  收藏  举报