利口1055. Shortest Way to Form String
如果相邻两个字符在同一个substring里,那么第一个字符在source的index要小于第二个字符。
解法1:
使用dp[i]记录[x,y]. x: shortest way to form target[0:i]; y: current index of target[i-1] in source。
dp的状态转移如何求得?假设dp[i-1]已经算好,我们令[x,y]=dp[i-1]。
如果target[i-1]不存在source中,那么直接返回-1。否则,假设dp[i]=[x',y'],
y'应该是target[i-1]在source的所有位置中,大于y的第一个,这里就会出现两种情况:
1)存在这样的y',那么x就等于x’(target[i-2]和target[i-1]在同一个substring上),dp[i]就算好了;
2)不存在这样的y',那么target[i-1]在source的所有位置都小于x,那么找到最小的位置并令其为y'。这就相当于从target[i-1]开始又重新开始了一条substring,target[i-2]和target[i-1]不在同一个substring上,x'=x+1
需要遍历target,对于每个target[i-1],最坏需要遍历整个source,找到对应位置。时间复杂度为O(len(target)*len(source))。代码如下:
1 def shortestWay(self, source, target): 2 m,n=len(source),len(target) 3 s=set() 4 for c in source: s.add(c) 5 #dp[i]: [x,y]. x: shortest way to form target[0:i]; y: index of target[i-1] in source 6 dp=[[None,None] for i in range(n+1)] 7 dp[0]=[1,-1] 8 for i in range(1,n+1): 9 x,y=dp[i-1] 10 if target[i-1] not in s: return -1 11 for j in range(y+1,m): 12 if source[j]==target[i-1]: 13 dp[i]=[x,j] 14 break 15 if dp[i][0]!=None: continue 16 for j in range(y+1): 17 if source[j]==target[i-1]: 18 dp[i]=[x+1,j] 19 break 20 return dp[n][0]
解法2:
由于状态dp[i]只依赖于dp[i-1],可以使用滚动数组优化空间。代码如下:
def shortestWay(self, source, target): m,n=len(source),len(target) uniqueS=set() for c in source: uniqueS.add(c) #dp[i]: [x,y]. x: shortest way to form target[0:i]; y: index of target[i-1] in source dp=[[None,None] for i in range(2)] now,old=0,1 dp[now]=[1,-1] for i in range(1,n+1): now,old=old,now if target[i-1] not in uniqueS: return -1 x,y=dp[old] flag=0 for j in range(y+1,m): if source[j]==target[i-1]: flag=1 dp[now]=[x,j] break if flag==1: continue for j in range(y+1): if source[j]==target[i-1]: dp[now]=[x+1,j] break return dp[now][0]
解法3:
我们可以先用一个dictionary把source里每个字符对应的位置都记录下来(这些位置自然是从小到大)。然后我们每次y'时,我们其实是在找一个sorted list里大于y的第一个数(如果没有就令y’为第一个元素),这个其实是可以用二分把时间复杂度从O(n)变成O(logn)。代码如下:
class Solution(object): def shortestWay(self, source, target): m,n=len(source),len(target) s={} for i,c in enumerate(source): if s.get(c)==None: s[c]=[i] else: s[c].append(i) #dp[i]: [x,y]. x: shortest way to form target[0:i]; y: index of target[i-1] in source dp=[[None,None] for i in range(2)] now,old=0,1 dp[now]=[1,-1] for c in target: now,old=old,now if c not in s: return -1 x,y=dp[old] if y>=s[c][-1]: dp[now]=[x+1,s[c][0]] else: j=self.binary_serach(s[c],y) dp[now]=[x,j] #print dp return dp[now][0] def binary_serach(self,nums,target): #return the first num in nums that larger than target l,r=0,len(nums)-1 ans=0 while l<=r: mid=l+(r-l)/2 if nums[mid]<=target: l=mid+1 else: ans=mid r=mid-1 return nums[ans]