leetcode 936. Stamping The Sequence

You want to form a target string of lowercase letters.

At the beginning, your sequence is target.length ‘?’ marks. You also have a stamp of lowercase letters.

On each turn, you may place the stamp over the sequence, and replace every letter in the sequence with the corresponding letter from the stamp. You can make up to 10 * target.length turns.

For example, if the initial sequence is “???”, and your stamp is “abc”, then you may make “abc??”, “?abc?”, “??abc” in the first turn. (Note that the stamp must be fully contained in the boundaries of the sequence in order to stamp.)

If the sequence is possible to stamp, then return an array of the index of the left-most letter being stamped at each turn. If the sequence is not possible to stamp, return an empty array.

For example, if the sequence is “ababc”, and the stamp is “abc”, then we could return the answer [0, 2], corresponding to the moves “???” -> “abc??” -> “ababc”.

Also, if the sequence is possible to stamp, it is guaranteed it is possible to stamp within 10 * target.length moves. Any answers specifying more than this number of moves will not be accepted.

Example 1:

Input: stamp = "abc", target = "ababc"
Output: [0,2]
([1,0,2] would also be accepted as an answer, as well 
as some other answers.)

Example 2:

Input: stamp = "abca", target = "aabcaca"
Output: [3,0,1]

Note:

  1. 1 <= stamp.length <= target.length <= 1000
  2. stamp and target only contain lowercase letters.

给定一个 string stamp,使其左右移动构成 string target ,移动时,可以覆盖当前位置上已有的元素,但也要保证 string stamp的完整(不能单独将字符串的某一段放入)。
根据题意可知,在 string target中,至少有一段与 string stamp完全相同,如果 string target可以由 string stamp构成,因为string target 由 string stamp 左右移动并覆盖已有元素组成的,那么在这段完全相同的部分下,也一定覆盖着一段string stamp的一部分,以此类推,就可以 以相反的顺序得到构成 string target的过程


class Solution {
private:
    int lenS,lenT;
    queue<int> Q;
    void ini(string &stamp,string &target){
        lenS=stamp.size();
        lenT=target.size();
    }
public:
    vector<int> movesToStamp(string stamp, string target) {
        vector<int> n;
        stack<int> store;
        ini(stamp,target);
        if(stamp[0]!=target[0])
            return n;
        auto stampLast=stamp.end()-1;
        auto targetLast=target.end()-1;
        if(*stampLast!=*targetLast)
            return n;
        vector<bool> match(lenT,false);
        int sum=0;
        queue<int> Q;
        for(int i=0;i<=lenT-lenS;i++)
            if(isSame(i,stamp,target)){
                Q.push(i);
            }
        int now;
        while(!Q.empty()){
            now=Q.front();
            Q.pop();
            if(match[now])
                continue;
            if(isSame(now,stamp,target)){
                match[now]=true;
                store.push(now);
                int star=now-lenS+1<0 ? 0:now-lenS+1;
                int lim=(now+lenS-1 > lenT-lenS) ? lenT-lenS:now+lenS-1;
                for(int i=star;i<= lim ; i++){
                    if(!match[i])
                        Q.push(i);
                }
                sum+=change(now,target);
            }
        }
        if(sum==lenT){
            vector<int> ans;
            while(!store.empty()){
                ans.push_back(store.top());
                store.pop();
            }
            return ans;
        }
        else
            return n;
    }
    bool isSame(int index,string &stamp,string &target){
        for(int i=index,j=0;i<index+lenS;i++,j++){
            if(target[i]=='?')
                continue;
            if(stamp[j]!=target[i])
                return false;
        }
        return true;
    }
    inline int change(int index,string &target){
        int num=0;
        for(int i=index;i<index+lenS;i++)
            if(target[i]!='?'){
                target[i]='?';
                num++;
            }
        return num;
    }
};

这是一个最原始的想法,没有进行优化。
由于多次检测匹配的操作,存在大量的重复计算,因此可以在第一次匹配时记录需要的数据,从而优化操作次数。

申明:
queue<int> Q 队列中元素为一段长度为lenS的字符串的起始位置
vector<bool> match 记录从位置 i 开始,长度为lenS的子串是否与 string stamp 匹配

先找到与 string stamp 相同的位置,在 string target 中标记相应的位置,说明这一段字符已经被匹配,且在该位置上可以为任意字符(stamp 终将覆盖这里),现考虑匹配字符下被覆盖的部分,首先声明在当前位置上长度为lenS的字符串匹配,再遍历所有会被 更改为覆盖位置 影响长度的lenS的子串,寻找新的匹配位置,往复循环,直到队列为空。最后判断在string target 中是否存在一个长度为lenS的子串不能匹配,若不存在,则可行;若存在,则不可行
Ps:根据压入队列的顺序,不难发现,一个位置可能在队列中出现多次,如果这个位置在某一次判定为匹配,可能导致这个位置重复输出,因此在取出位置时,先判断这一位置是否已经被判定为匹配。


class Solution {
private:
    int lenS,lenT;
    void ini(string &stamp,string &target){
        lenS=stamp.size();
        lenT=target.size();
    }
    struct Node{
        set<int> match,todo;
        Node(){};
        Node(set<int> &m,set<int> &t):match(m),todo(t){};
    };
public:
    vector<int> movesToStamp(string stamp, string target) {
        ini(stamp,target);
        vector<bool> change(lenT,false);
        queue<int> Q;
        stack<int> store;
        vector<Node> record;
        
        for(int i=0;i<=lenT-lenS;i++){
            set<int> match,todo;
            
            for(int j=0;j<lenS;j++)
                if(target[i+j]==stamp[j])
                    match.insert(i+j);
                else
                    todo.insert(i+j);
            
            record.push_back(Node(match,todo));
            
            if(todo.empty()){
                store.push(i);
                for(int j=0;j<lenS;j++)
                    if(!change[j+i]){
                        Q.push(i+j);
                        change[i+j]=true;
                    }
            }
            
        }
        
        int now;
        while(!Q.empty()){
            now=Q.front();
            Q.pop();
            
            for(int i=max(0,now-lenS+1);i<=min(lenT-lenS,now);i++)
                if(record[i].todo.find(now)!=record[i].todo.end()){
                    record[i].todo.erase(now);
                    if(record[i].todo.empty()){
                        store.push(i);
                        for(auto ele : record[i].match)
                            if(!change[ele]){
                                Q.push(ele);
                                change[ele]=true;
                            }
                    }
                }
        }
        vector<int> ans;
        for(auto ele : change)
            if(!ele)
                return ans;
        while(!store.empty()){
            ans.push_back(store.top());
            store.pop();
        }
        return ans;
    }
};

这个是根据题解写的,只进行一次字符串匹配的循环,在时间进行了优化,相应的内存消耗增加。
记录每一个位置 i 对应长度为lenS的字符串上有哪些部分是能对应的,哪些些是不对应的。再将完全对应的字符串的位置全部压入队列。
在队列中的每一个位置都是已匹配位置,在change中声明当前位置已匹配,之后考虑当前位置改动后可能会产生影响的所有位置 i 对应的长度为lenS的字符串。

猜你喜欢

转载自blog.csdn.net/white_156/article/details/83748195
今日推荐