LeetCode 1055. Shortest Way to Form String 字符串贪心 NextMatch数组

From any string, we can form a subsequence of that string by deleting some number of characters (possibly no deletions).

Given two strings source and target, return the minimum number of subsequences of source such that their concatenation equals target. If the task is impossible, return -1.

 

Example 1:

Input: source = "abc", target = "abcbc"
Output: 2
Explanation: The target "abcbc" can be formed by "abc" and "bc", which are subsequences of source "abc".

Example 2:

Input: source = "abc", target = "acdbc"
Output: -1
Explanation: The target string cannot be constructed from the subsequences of source string due to the character "d" in target string.

Example 3:

Input: source = "xyz", target = "xzyxz"
Output: 3
Explanation: The target string can be constructed as follows "xz" + "y" + "xz".

 

Constraints:

  • Both the source and target strings consist of only lowercase English letters from "a"-"z".
  • The lengths of source and target string are between 1 and 1000.

---------------------------------------------------------------------------------------------

这题据说和LeetCode 792很像,上来如果能迅速想到贪心,这题就成功一半,否则写了一个BFS,差点超时。当然贪心的证明比较容易,最后就成了消耗了source完整几次+半次的问题。

直接贪心1:

class Solution:
    def shortestWay(self, source: str, target: str) -> int:
        slen,tlen = len(source),len(target)
        si,res = 0,0
        for ti in range(tlen):
            #print("si={0}".format(si))
            i = 0
            while (i < slen):
                idx = (i+si)%slen
                if (idx+1 == slen):
                    res+=1
                if (source[idx] == target[ti]):
                    si = (idx+1)%slen
                    break
                i+=1
            if (i == slen):
                return -1
        return res if si == 0 else res+1

对于Python这种慢语言,字符串操作是比较慢的,用一些find函数会好很多,因此:

直接贪心2:

class Solution(object):
    def shortestWay(self, source, target):
        """
        :type source: str
        :type target: str
        :rtype: int
        """
        source_cursor = 0
        round_count = 1
        for c in target:
            idx = source.find(c, source_cursor)
            if idx == -1:
                round_count += 1
                idx = source.find(c)
                if idx == -1:
                    return -1
            
            source_cursor = idx+1
        
        return round_count

这些还有几种优化的思路,最直接的方法是建立字符到位置的映射。例如source="abcab",那么a->[0,3], b->[1,4],c->[2],对于每个List,可以二分,这样复杂度是tlen*log(slen),python3里,发现不二分也很快:

数组映射法(Python最快的结果):

class Solution(object):
    def shortestWay(self, source: str, target: str) -> int:
            # construct a hashmap
            hashmap = collections.defaultdict(list)
            for i, s in enumerate(source):
                hashmap[s].append(i)
            result, idx = 1, -1
            for c in target:
                if c not in hashmap: return -1
                else:
                    indicator = False
                    for i in hashmap[c]:
                        if i > idx: 
                            idx = i
                            indicator = True
                            break
                    if not indicator:
                        idx = hashmap[c][0]
                        result += 1     
            return result

在这个基础上还有进一步的优化,例如source="abcab",那么a->[0,3], b->[1,4],c->[2],刚才映射成了list。如果不用list,用f[ch][pos]表示在source串中pos位置(包含pos位置)以后找ch的下一个pos,那么source="abcab"就会变成:

	a	b	c
0	1	
1	4	
2	4	
3	4	
4	1	

先算这个矩阵的思路比较巧妙,注意不要写成O(slen*slen)的写法,完全可以在26*O(slen)的复杂度内完成,以下是代码,注释部分是刚开始写的O(slen*slen)的代码,非常慢,但是即使最终是O(slen)+O(tlen)的复杂度,Python并没有速度优势:

from collections import defaultdict

class Solution:
    def shortestWay(self, source: str, target: str) -> int:
        slen, tlen = len(source), len(target)
        dic = defaultdict(lambda: [slen for i in range(slen)])
        # for i in range(slen):
        #     ch = source[i]
        #     dic[ch][i] = (i + 1) % slen
        #     for j in range(1, slen):
        #         if (ch == source[i - j]):
        #             break
        #         else:
        #             dic[ch][(i - j + slen) % slen] = (i + 1) % slen
        for i in range(slen):
            dic[source[i]][i] = (i+1)%slen
        for ch in set(list(source)):
            prev = slen
            for j in range(slen-1,-slen,-1):
                if (dic[ch][j] == slen):
                    dic[ch][j] = prev
                else:
                    prev = dic[ch][j]

        #print(dic)
        si, cross_end = 0, 0  # si是基准pos
        for ti in range(tlen):
            if (target[ti] in dic):
                nxt_si = dic[target[ti]][si]
                cross_end = cross_end + 1 if nxt_si < si else cross_end
                si = nxt_si
            else:
                return -1
        return cross_end if si == 0 else cross_end + 1

s = Solution()
print(s.shortestWay("xyz","xzyxz"))

后记:求NextMatch数组如果把行列颠倒一下,每次把整个26个字母的位置都拷贝一下,写起来就不用像上面这么拧巴,可以参考https://blog.youkuaiyun.com/taoqick/article/details/106735554的思路。

class Solution(object):
    def minWindow(self, S, T):
        slen,tlen,c,n = len(S),len(T),0,1
        nxt = [-1 for i in range(26)]
        nxt_arr = [None for i in range(slen)]
 
        for i in range(slen-1, -1, -1):
            ch_idx = ord(S[i])-ord('a')
            nxt[ch_idx] = i
            nxt_arr[i] = tuple(nxt)

 

### 回答1: Active Directory服务是种由微软公司开发的网络服务,它提供了种集中管理和控制网络资源的方式。它可以在中集中管理用户、计算机、应用程序和其他网络资源,从而提高了网络的安全性和可管理性。Active Directory服务还提供了些高级功能,如单点登录、组策略管理和名系统(DNS)集成等,使得网络管理员可以更加轻松地管理和维护网络。 ### 回答2: Active Directory服务(Active Directory Domain Services,简称AD DS)是微软公司的项用于管理和组织网络资源的目录服务。它是种基于LDAP(轻量级目录访问协议)的目录服务,可以让用户和管理员方便地管理和访问网络中的资源。 AD DS的主要功能包括用户身份认证、访问控制、组管理和资源管理等。通过AD DS,管理员可以集中管理和配置用户和计算机的访问权限,确保系统安全。同时,AD DS还提供了的集中管理功能,管理员可以通过控制器管理中的所有对象,并在中实施策略。 AD DS还支持单点登录功能,用户只需在登录到之后,即可自动访问到所属中的资源,而无需再次输入用户名和密码。这大大提高了用户的工作效率。 此外,AD DS还支持多架构,可以通过建立信任关系实现跨资源的访问和管理。管理员可以维护多个之间的信任关系,实现用户和资源的统管理。 总而言之,AD DS是种强大的目录服务,可以实现用户和资源的集中管理和访问控制,提高网络系统的稳定性和安全性。它是企业网络管理的重要组成部分,为企业提供了高效的身份认证和资源管理功能,增强了企业的生产力和安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值