先递归吧。果不其然,字符串的时候就超时了。毕竟是指数级。
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);
}
}
所以新标准的写法真是个好东西。