利口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]
View Code

解法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]
View Code

解法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]
View Code
posted @ 2020-01-07 14:04  热锅上的码裔  阅读(213)  评论(0编辑  收藏  举报