Leetcode第514题 自由之路C++解法

先递归吧。果不其然,字符串的时候就超时了。毕竟是指数级。

class Solution {
    
    
public:
    int findDis(string& ring,int pos,string& key,int pmove)
    {
    
    
        int m=ring.size(),n=key.size();
        if(pmove==n)
        return 0;
        if(ring[pos]==key[pmove])
                return 1+findDis(ring,pos,key,pmove+1);
        else
        {
    
    
            int clk[2],step[2];
            step[0]=1,step[1]=m-1;

            for(int i=0;i<2;++i)
            {
    
    
                int next=pos;
                for(int j=1;j<m;++j)
                {
    
    
                    next=(next+step[i])%m;
                    if(ring[next]==key[pmove]){
    
    
                        clk[i]=next;
                        step[i]=j;
                        break;}
                }
            }
            return min(1+step[0]+findDis(ring,clk[0],key,pmove+1),
                    1+step[1]+findDis(ring,clk[1],key,pmove+1));
        } 
    }

    int findRotateSteps(string ring, string key) {
    
    
        return findDis(ring,0,key,0);    
    }
};

想了好久剪枝发现都减不掉。只能学习答案的DP,这个DP确实不同以往,以往的DP的状态转移方程一般是固定的两三个,这个则需要上一行的若干个

class Solution {
    
    
public:

    int findRotateSteps(string ring, string key) {
    
    
        int n=ring.size(),m=key.size();
        // vector<vector<int>> dp(m+1,vector<int>line(n,0x3f3f3f3f));
        vector<vector<int>> dp(m+1, vector<int>(n, m*n));//这里也可以设置为一个0x3f3f3f。设置m*n道理很简单,因为每次最多转n/2次,所以m*n必然达不到,相当于这里一个达不到的极大值
        for(int i=0;i<n;++i) dp[0][i]=min(i,n-i);//上个pos,需要保存其他位置与0指针的最小距离,用于计算dp[1]行
        for(int i=1;i<=m;++i)
        {
    
    
            vector<int> pos(dp[i-1].begin(),dp[i-1].end());
            // vector<int> pos(dp[i-1][0],dp[i-1][n-1]);初始化上个pos方法,要用迭代器而不是序号
            for(int j=0;j<n;++j)
            {
    
    //只有ring[j]==key[i-1]的时候对第i行的dp[i][j]更新
                if(ring[j]==key[i-1])
                {
    
    //而对dp[i][j]需要知道上一个字符key[i-1]匹配的所有情况
                    for(int k=0;k<n;++k)
                    {
    
    //step表示现在的位置j与上一个字符匹配时候的位置k之间的步数
                        int step=abs(j-k);
                        dp[i][j]=min(dp[i][j],
                        pos[k]+min(step,n-step)+1);
                        //当然不用pos数组也可以,因为pos数组相当于dp[i-1]行                        
                        //dp[i][j]=min(dp[i][j],dp[i-1][k]+min(step,n-step)+1);
                    }
                }
            }
        }
        return *min_element(dp[m].begin(),dp[m].end());
    }
};

而如果要优化空间复杂度,就不能只用一个数组了,因为每一行数据需要上一行的所有数据,而不仅仅是像普通字符串题那样的前一个,所以可以用两个数组pre(n)和now(n);每一行结束后用now去代替pre即可。

for(int i=0;i<m;++i){
    
    
	vector<int> now(n,m*n);
	for(int j=0;j<n;++j)
	{
    
    	
	.......
	}
	pre=now;
}

对比答案,总算明白了到底怎么回事,我说时间复杂度怎么差了一半,其实问题就在于我们内层循环值更新ring[j]==key[i]的情况,但是如果简单的把dp数组设置为ring.size()的情况的话,实际循环复杂度就确实是O(MN^2)了。
所以最好就是把这个优化掉,每次直接进入ring[j]==key[i]的数组。
基于此,重新做了下面。实际上除了用一个pos[26]去记录ring中每一个字母的位置外,也可以用哈希表unordered<char,vector<int>> hashmap即可

class Solution {
    
    
public:

    int findRotateSteps(string ring, string key) {
    
    
        int n=ring.size(),m=key.size();
        vector<int> pos[26];
        for(int i=0;i<n;++i) pos[ring[i]-'a'].push_back(i);
        vector<int> pre(n);
        for(int i:pos[key[0]-'a'])
            pre[i]=min(i,n-i)+1;    
        for(int i=1;i<m;++i)
        {
    
    
            vector<int> now(n,m*n);
            for(int j:pos[key[i]-'a'])
            {
    
    
                for(int k:pos[key[i-1]-'a'])
                    {
    
    
                        int step=abs(k-j);
                        now[j]=min(now[j],
                            pre[k]+min(step,n-step)+1);
                    }
            }
	       pre=now;
        }
        return *min_element(pre.begin(),pre.end());
    }
};

这里使用这种C++新规定,方便了很多,因为我们关注的是每个元素值而不是元素序号,如果用序号的话,就比较麻烦,如下

for(int j=0;j<pos[key[i]-'a'].size();++j)
	for(int k=0;k<pos[key[i-1]-'a'].size();++k)
    	{
    
    
    		int step=abs(pospos[key[i]-'a'][j]-pos[key[i-1]-'a'][k]);
            now[pospos[key[i]-'a'][j]]=min(now[pospos[key[i]-'a'][j]],
                        pre[pos[key[i-1]-'a'][k]]+min(step,n-step)+1);
    	}

或者多加一个变量,只是好懂了那么一丁点

for(int j=0;j<pos[key[i]-'a'].size();++j)
{
    
    
	int npos=pos[key[i]-'a'][j];
	for(int k=0;k<pos[key[i-1]-'a'].size();++k)
    	{
    
    
    		int ppos=pos[key[i-1]-'a'][k];
    		int step=abs(npos-ppos);
            now[npos]=min(now[npos],
                        pre[ppos]+min(step,n-step)+1);
    	}
}

所以新标准的写法真是个好东西。

猜你喜欢

转载自blog.csdn.net/meixingshi/article/details/113933336