String 从字符串中删除所有出现的子字符串

String 从字符串中删除所有出现的子字符串,string,algorithm,string-matching,string-algorithm,String,Algorithm,String Matching,String Algorithm,给定一个字符串S和一组n个子字符串。从S中删除这些n个子字符串的每个实例,使S具有最小长度,并输出该最小长度 例1 输出 说明: ccdaabcdbb -> ccdacdbb -> cabb -> cb (length=2) 例2 输出 如何解决这个问题?一个简单的算法是: 对于每个子字符串,尝试所有可能的方法将其从字符串中删除,然后递归。 在: 设复杂性为FS,n,l,其中S是输入字符串的长度,n是集合子字符串的基数,l是子字符串的特征长度。然后 F(S,n,l) ~ n

给定一个字符串S和一组n个子字符串。从S中删除这些n个子字符串的每个实例,使S具有最小长度,并输出该最小长度

例1

输出

说明:

ccdaabcdbb -> ccdacdbb -> cabb -> cb (length=2)
例2

输出

如何解决这个问题?

一个简单的算法是:

对于每个子字符串,尝试所有可能的方法将其从字符串中删除,然后递归。 在:

设复杂性为FS,n,l,其中S是输入字符串的长度,n是集合子字符串的基数,l是子字符串的特征长度。然后

F(S,n,l) ~ n * ( S * l + F(S-l,n,l) )
看起来它最多是OS^2*n*l.

一个简单的算法是:

对于每个子字符串,尝试所有可能的方法将其从字符串中删除,然后递归。 在:

设复杂性为FS,n,l,其中S是输入字符串的长度,n是集合子字符串的基数,l是子字符串的特征长度。然后

F(S,n,l) ~ n * ( S * l + F(S-l,n,l) )

看起来它最多是OS^2*n*l.

如果您希望获得原始性能,并且字符串非常大,那么您可以做得比暴力更好。使用后缀trie(例如Ukkonnen trie)来存储字符串。然后找到我们在Om时间内完成的每个子串,m是子串长度,并将子串的偏移量和长度存储在一个数组中。 然后使用偏移量和长度信息,通过使用C中的\0或其他占位符字符填充这些区域,实际删除子字符串。通过计算所有非空字符,您将获得字符串的最小长度


这将处理重叠的子字符串,例如,假设您的字符串是abcd,并且您有两个子字符串ab和abcd。

如果您希望获得原始性能,并且字符串非常大,那么您可以做得比暴力更好。使用后缀trie(例如Ukkonnen trie)来存储字符串。然后找到我们在Om时间内完成的每个子串,m是子串长度,并将子串的偏移量和长度存储在一个数组中。 然后使用偏移量和长度信息,通过使用C中的\0或其他占位符字符填充这些区域,实际删除子字符串。通过计算所有非空字符,您将获得字符串的最小长度


这将处理重叠的子字符串,例如,假设您的字符串是abcd,并且您有两个子字符串ab和abcd。

以下解决方案的复杂性为Om*n,其中m=lenS,n是子字符串的数目

def foo(S, sub):
    i = 0
    while i < len(S):
        for e in sub:
            if S[i:].startswith(e):
                S = S[:i] + S[i+len(e):]
                i -= 1
                break
        else: i += 1
    return S, i

下面的解决方案的复杂度为Om*n,其中m=lenS,n是子串的数目

def foo(S, sub):
    i = 0
    while i < len(S):
        for e in sub:
            if S[i:].startswith(e):
                S = S[:i] + S[i+len(e):]
                i -= 1
                break
        else: i += 1
    return S, i
我用trie+dp解决了这个问题。 首先在trie中插入子字符串。然后定义DP的状态是一些字符串,遍历该字符串并考虑每个i i=0。s、 长度作为某个子字符串的开始。设j=i并增加j,只要trie中有一个后缀,它肯定会将您带到至少一个子字符串,如果您在某个子字符串之间有公共后缀,例如abce和abdd,当您遇到某个子字符串的结尾时,它可能会更多,去解决新的子问题,找到所有子串缩减之间的最小值

这是我的代码。不要担心代码的长度。只需阅读solve函数并忘记路径,我将其包含在打印所形成的字符串中

struct node{
    node* c[26];
    bool str_end;
    node(){
        for(int i= 0;i<26;i++){
            c[i]=NULL;
        }
        str_end= false;
    }
};
class Trie{
public:
    node* root;
    Trie(){
        root = new node();
    }
    ~Trie(){
        delete root;
    }
};
class Solution{
public:
    typedef pair<int,int>ii;
    string get_str(string& s,map<string,ii>&path){
        if(!path.count(s)){
            return s;
        }
        int i= path[s].first;
        int j= path[s].second;
        string new_str =(s.substr(0,i)+s.substr(j+1));
        return get_str(new_str,path);
    }
    int solve(string& s,Trie* &t, map<string,int>&dp,map<string,ii>&path){
        if(dp.count(s)){
            return dp[s];
        }
        int mn= (int)s.length();
        for(int i =0;i<s.length();i++){
            string left = s.substr(0,i);
            node* cur = t->root->c[s[i]-97];
            int j=i;
            while(j<s.length()&&cur!=NULL){
                if(cur->str_end){
                    string new_str =left+s.substr(j+1);
                    int ret= solve(new_str,t,dp,path);
                    if(ret<mn){
                        path[s]={i,j};
                    }
                }
                cur = cur->c[s[++j]-97];
            }
        }
        return dp[s]=mn;
    }
    string removeSubstrings(vector<string>& substrs, string s){
        map<string,ii>path;
        map<string,int>dp;
        Trie*t = new Trie();
        for(int i =0;i<substrs.size();i++){
            node* cur = t->root;
            for(int j=0;j<substrs[i].length();j++){
                if(cur->c[substrs[i][j]-97]==NULL){
                    cur->c[substrs[i][j]-97]= new node();
                }
                cur = cur->c[substrs[i][j]-97];
                if(j==substrs[i].length()-1){
                    cur->str_end= true;
                }
            }
        }
        solve(s,t,dp,path);
        return get_str(s, path);
    }
};

int main(){
    vector<string>substrs;
    substrs.push_back("ab");
    substrs.push_back("cd");
    Solution s;
    cout << s.removeSubstrings(substrs,"ccdaabcdbb")<<endl;
    return 0;
}
我用trie+dp解决了这个问题。 首先在trie中插入子字符串。然后定义DP的状态是一些字符串,遍历该字符串并考虑每个i i=0。s、 长度作为某个子字符串的开始。设j=i并增加j,只要trie中有一个后缀,它肯定会将您带到至少一个子字符串,如果您在某个子字符串之间有公共后缀,例如abce和abdd,当您遇到某个子字符串的结尾时,它可能会更多,去解决新的子问题,找到所有子串缩减之间的最小值

这是我的代码。不要担心代码的长度。只需阅读solve函数并忘记路径,我将其包含在打印所形成的字符串中

struct node{
    node* c[26];
    bool str_end;
    node(){
        for(int i= 0;i<26;i++){
            c[i]=NULL;
        }
        str_end= false;
    }
};
class Trie{
public:
    node* root;
    Trie(){
        root = new node();
    }
    ~Trie(){
        delete root;
    }
};
class Solution{
public:
    typedef pair<int,int>ii;
    string get_str(string& s,map<string,ii>&path){
        if(!path.count(s)){
            return s;
        }
        int i= path[s].first;
        int j= path[s].second;
        string new_str =(s.substr(0,i)+s.substr(j+1));
        return get_str(new_str,path);
    }
    int solve(string& s,Trie* &t, map<string,int>&dp,map<string,ii>&path){
        if(dp.count(s)){
            return dp[s];
        }
        int mn= (int)s.length();
        for(int i =0;i<s.length();i++){
            string left = s.substr(0,i);
            node* cur = t->root->c[s[i]-97];
            int j=i;
            while(j<s.length()&&cur!=NULL){
                if(cur->str_end){
                    string new_str =left+s.substr(j+1);
                    int ret= solve(new_str,t,dp,path);
                    if(ret<mn){
                        path[s]={i,j};
                    }
                }
                cur = cur->c[s[++j]-97];
            }
        }
        return dp[s]=mn;
    }
    string removeSubstrings(vector<string>& substrs, string s){
        map<string,ii>path;
        map<string,int>dp;
        Trie*t = new Trie();
        for(int i =0;i<substrs.size();i++){
            node* cur = t->root;
            for(int j=0;j<substrs[i].length();j++){
                if(cur->c[substrs[i][j]-97]==NULL){
                    cur->c[substrs[i][j]-97]= new node();
                }
                cur = cur->c[substrs[i][j]-97];
                if(j==substrs[i].length()-1){
                    cur->str_end= true;
                }
            }
        }
        solve(s,t,dp,path);
        return get_str(s, path);
    }
};

int main(){
    vector<string>substrs;
    substrs.push_back("ab");
    substrs.push_back("cd");
    Solution s;
    cout << s.removeSubstrings(substrs,"ccdaabcdbb")<<endl;
    return 0;
}

问题的规模是多少?字符串的长度,子字符串的数量是多少?BFS可以解决这个问题,但对于大型企业来说可能不够有效strings@amit我猜BFS会采用O | S | ^2*| SET |字符串长度的问题规模是多少,子字符串的数量是多少?BFS可以解决这个问题,但对于大型企业来说可能不够有效strings@amit我想BFS需要O | S | ^2*| SET |嗨,你能详细介绍一下算法吗?应该是beg+=1。例如,考虑输入= ABABACC和子串= [ABA,ABC]的情况。原始解决方案将产生ABABAC->bacc,但正确的解决方案应该是ABABAC->abcc->c。此算法的最坏情况时间复杂度是多少?@udbhavkalla:看起来最多是OS^2*n*l。嗨,c您能详细说明一下算法吗?应该是beg+=1。例如,考虑输入= ABABACC和子串= [ABA,ABC]的情况。最初的解决方案将产生ABABAC->bacc,但正确的解决方案是这样的
解应该是ababac->abcc->c。这个算法最坏的时间复杂度是多少?@udbhavkalla:看起来它最多是OS^2*n*l。
def foo(S, sub):
    i = 0
    while i < len(S):
        for e in sub:
            if S[i:].startswith(e):
                S = S[:i] + S[i+len(e):]
                i -= 1
                break
        else: i += 1
    return S, i
struct node{
    node* c[26];
    bool str_end;
    node(){
        for(int i= 0;i<26;i++){
            c[i]=NULL;
        }
        str_end= false;
    }
};
class Trie{
public:
    node* root;
    Trie(){
        root = new node();
    }
    ~Trie(){
        delete root;
    }
};
class Solution{
public:
    typedef pair<int,int>ii;
    string get_str(string& s,map<string,ii>&path){
        if(!path.count(s)){
            return s;
        }
        int i= path[s].first;
        int j= path[s].second;
        string new_str =(s.substr(0,i)+s.substr(j+1));
        return get_str(new_str,path);
    }
    int solve(string& s,Trie* &t, map<string,int>&dp,map<string,ii>&path){
        if(dp.count(s)){
            return dp[s];
        }
        int mn= (int)s.length();
        for(int i =0;i<s.length();i++){
            string left = s.substr(0,i);
            node* cur = t->root->c[s[i]-97];
            int j=i;
            while(j<s.length()&&cur!=NULL){
                if(cur->str_end){
                    string new_str =left+s.substr(j+1);
                    int ret= solve(new_str,t,dp,path);
                    if(ret<mn){
                        path[s]={i,j};
                    }
                }
                cur = cur->c[s[++j]-97];
            }
        }
        return dp[s]=mn;
    }
    string removeSubstrings(vector<string>& substrs, string s){
        map<string,ii>path;
        map<string,int>dp;
        Trie*t = new Trie();
        for(int i =0;i<substrs.size();i++){
            node* cur = t->root;
            for(int j=0;j<substrs[i].length();j++){
                if(cur->c[substrs[i][j]-97]==NULL){
                    cur->c[substrs[i][j]-97]= new node();
                }
                cur = cur->c[substrs[i][j]-97];
                if(j==substrs[i].length()-1){
                    cur->str_end= true;
                }
            }
        }
        solve(s,t,dp,path);
        return get_str(s, path);
    }
};

int main(){
    vector<string>substrs;
    substrs.push_back("ab");
    substrs.push_back("cd");
    Solution s;
    cout << s.removeSubstrings(substrs,"ccdaabcdbb")<<endl;
    return 0;
}