@@ -188,32 +188,339 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3500-3599/3597.Pa
188188
189189<!-- solution:start -->
190190
191- ### 方法一
191+ ### 方法一:哈希表 + 模拟
192+
193+ 我们可以用一个哈希表 $\textit{vis}$ 来记录已经出现过的段。然后我们遍历字符串 $s$,逐字符构建当前段 $t$,直到该段之前未曾出现过。每当我们构建出一个新的段时,就将其加入到结果列表中,并将其标记为已经出现过。
194+
195+ 遍历结束后,返回结果列表即可。
196+
197+ 时间复杂度 $O(n \times \sqrt{n})$,空间复杂度 $O(n)$,其中 $n$ 是字符串 $s$ 的长度。
192198
193199<!-- tabs:start -->
194200
195201#### Python3
196202
197203``` python
198-
204+ class Solution :
205+ def partitionString (self , s : str ) -> List[str ]:
206+ vis = set ()
207+ ans = []
208+ t = " "
209+ for c in s:
210+ t += c
211+ if t not in vis:
212+ vis.add(t)
213+ ans.append(t)
214+ t = " "
215+ return ans
199216```
200217
201218#### Java
202219
203220``` java
204-
221+ class Solution {
222+ public List<String > partitionString (String s ) {
223+ Set<String > vis = new HashSet<> ();
224+ List<String > ans = new ArrayList<> ();
225+ String t = " " ;
226+ for (char c : s. toCharArray()) {
227+ t += c;
228+ if (vis. add(t)) {
229+ ans. add(t);
230+ t = " " ;
231+ }
232+ }
233+ return ans;
234+ }
235+ }
205236```
206237
207238#### C++
208239
209240``` cpp
241+ class Solution {
242+ public:
243+ vector<string > partitionString(string s) {
244+ unordered_set<string > vis;
245+ vector<string > ans;
246+ string t = "";
247+ for (char c : s) {
248+ t += c;
249+ if (!vis.contains(t)) {
250+ vis.insert(t);
251+ ans.push_back(t);
252+ t = "";
253+ }
254+ }
255+ return ans;
256+ }
257+ };
258+ ```
259+
260+ #### Go
261+
262+ ```go
263+ func partitionString(s string) (ans []string) {
264+ vis := make(map[string]bool)
265+ t := ""
266+ for _, c := range s {
267+ t += string(c)
268+ if !vis[t] {
269+ vis[t] = true
270+ ans = append(ans, t)
271+ t = ""
272+ }
273+ }
274+ return
275+ }
276+ ```
277+
278+ #### TypeScript
279+
280+ ``` ts
281+ function partitionString(s : string ): string [] {
282+ const vis = new Set <string >();
283+ const ans: string [] = [];
284+ let t = ' ' ;
285+ for (const c of s ) {
286+ t += c ;
287+ if (! vis .has (t )) {
288+ vis .add (t );
289+ ans .push (t );
290+ t = ' ' ;
291+ }
292+ }
293+ return ans ;
294+ }
295+ ```
296+
297+ <!-- tabs: end -->
298+
299+ <!-- solution: end -->
300+
301+ <!-- solution: start -->
302+
303+ ### 方法二:字符串哈希 + 哈希表 + 模拟
210304
305+ 我们可以使用字符串哈希来加速段的查找。具体地,我们可以为每个段计算一个哈希值,并将其存储在一个哈希表中。这样,我们就可以在常数时间内判断一个段是否已经出现过。
306+
307+ 具体地,我们首先根据字符串 $s$ 创建一个字符串哈希类 $\textit{Hashing}$,该类支持计算字符串的哈希值。然后,我们遍历字符串 $s$,用两个指针 $l$ 和 $r$ 来表示当前段的起始和结束位置(下标从 $1$ 开始)。每次扩展 $r$,我们计算当前段的哈希值 $x$,如果该哈希值不在哈希表中,则将其加入结果列表,并将其哈希值标记为已经出现过。否则,我们继续扩展 $r$,直到找到一个新的段。
308+
309+ 遍历结束后,返回结果列表即可。
310+
311+ 时间复杂度 $O(n)$,空间复杂度 $O(n)$,其中 $n$ 是字符串 $s$ 的长度。
312+
313+ <!-- tabs: start -->
314+
315+ #### Python3
316+
317+ ``` python
318+ class Hashing :
319+ __slots__ = [" mod" , " h" , " p" ]
320+
321+ def __init__ (
322+ self , s : Union[str , List[str ]], base : int = 13331 , mod : int = 998244353
323+ ):
324+ self .mod = mod
325+ self .h = [0 ] * (len (s) + 1 )
326+ self .p = [1 ] * (len (s) + 1 )
327+ for i in range (1 , len (s) + 1 ):
328+ self .h[i] = (self .h[i - 1 ] * base + ord (s[i - 1 ])) % mod
329+ self .p[i] = (self .p[i - 1 ] * base) % mod
330+
331+ def query (self , l : int , r : int ) -> int :
332+ return (self .h[r] - self .h[l - 1 ] * self .p[r - l + 1 ]) % self .mod
333+
334+
335+ class Solution :
336+ def partitionString (self , s : str ) -> List[str ]:
337+ hashing = Hashing(s)
338+ vis = set ()
339+ l = 1
340+ ans = []
341+ for r, c in enumerate (s, 1 ):
342+ x = hashing.query(l, r)
343+ if x not in vis:
344+ vis.add(x)
345+ ans.append(s[l - 1 : r])
346+ l = r + 1
347+ return ans
348+ ```
349+
350+ #### Java
351+
352+ ``` java
353+ class Hashing {
354+ private final long [] p;
355+ private final long [] h;
356+ private final long mod;
357+
358+ public Hashing (String word ) {
359+ this (word, 13331 , 998244353 );
360+ }
361+
362+ public Hashing (String word , long base , int mod ) {
363+ int n = word. length();
364+ p = new long [n + 1 ];
365+ h = new long [n + 1 ];
366+ p[0 ] = 1 ;
367+ this . mod = mod;
368+ for (int i = 1 ; i <= n; i++ ) {
369+ p[i] = p[i - 1 ] * base % mod;
370+ h[i] = (h[i - 1 ] * base + word. charAt(i - 1 )) % mod;
371+ }
372+ }
373+
374+ public long query (int l , int r ) {
375+ return (h[r] - h[l - 1 ] * p[r - l + 1 ] % mod + mod) % mod;
376+ }
377+ }
378+
379+ class Solution {
380+ public List<String > partitionString (String s ) {
381+ Hashing hashing = new Hashing (s);
382+ Set<Long > vis = new HashSet<> ();
383+ List<String > ans = new ArrayList<> ();
384+ for (int l = 1 , r = 1 ; r <= s. length(); ++ r) {
385+ long x = hashing. query(l, r);
386+ if (vis. add(x)) {
387+ ans. add(s. substring(l - 1 , r));
388+ l = r + 1 ;
389+ }
390+ }
391+ return ans;
392+ }
393+ }
394+ ```
395+
396+ #### C++
397+
398+ ``` cpp
399+ class Hashing {
400+ private:
401+ vector<long long > p;
402+ vector<long long > h;
403+ long long mod;
404+
405+ public:
406+ Hashing(const string& word, long long base = 13331, long long mod = 998244353) {
407+ int n = word.size();
408+ p.resize(n + 1);
409+ h.resize(n + 1);
410+ p[ 0] = 1;
411+ this->mod = mod;
412+ for (int i = 1; i <= n; i++) {
413+ p[ i] = (p[ i - 1] * base) % mod;
414+ h[ i] = (h[ i - 1] * base + word[ i - 1] ) % mod;
415+ }
416+ }
417+
418+ long long query(int l, int r) const {
419+ return (h[r] - h[l - 1] * p[r - l + 1] % mod + mod) % mod;
420+ }
421+ };
422+
423+ class Solution {
424+ public:
425+ vector<string > partitionString(const string& s) {
426+ Hashing hashing(s);
427+ unordered_set<long long > vis;
428+ vector<string > ans;
429+ int l = 1;
430+ for (int r = 1; r <= (int) s.size(); ++r) {
431+ long long x = hashing.query(l, r);
432+ if (!vis.contains(x)) {
433+ vis.insert(x);
434+ ans.push_back(s.substr(l - 1, r - l + 1));
435+ l = r + 1;
436+ }
437+ }
438+ return ans;
439+ }
440+ };
211441```
212442
213443#### Go
214444
215445```go
446+ type Hashing struct {
447+ p, h []int64
448+ mod int64
449+ }
450+
451+ func NewHashing(s string, base, mod int64) *Hashing {
452+ n := len(s)
453+ p := make([]int64, n+1)
454+ h := make([]int64, n+1)
455+ p[0] = 1
456+ for i := 1; i <= n; i++ {
457+ p[i] = p[i-1] * base % mod
458+ h[i] = (h[i-1]*base + int64(s[i-1])) % mod
459+ }
460+ return &Hashing{p, h, mod}
461+ }
462+
463+ func (hs *Hashing) Query(l, r int) int64 {
464+ return (hs.h[r] - hs.h[l-1]*hs.p[r-l+1]%hs.mod + hs.mod) % hs.mod
465+ }
466+
467+ func partitionString(s string) (ans []string) {
468+ n := len(s)
469+ hashing := NewHashing(s, 13331, 998244353)
470+ vis := make(map[int64]bool)
471+ l := 1
472+ for r := 1; r <= n; r++ {
473+ x := hashing.Query(l, r)
474+ if !vis[x] {
475+ vis[x] = true
476+ ans = append(ans, s[l-1:r])
477+ l = r + 1
478+ }
479+ }
480+ return
481+ }
482+ ```
216483
484+ #### TypeScript
485+
486+ ``` ts
487+ class Hashing {
488+ private p: bigint [];
489+ private h: bigint [];
490+ private mod: bigint ;
491+
492+ constructor (s : string , base : bigint = 13331n , mod : bigint = 998244353n ) {
493+ const n = s .length ;
494+ this .mod = mod ;
495+ this .p = new Array <bigint >(n + 1 ).fill (1n );
496+ this .h = new Array <bigint >(n + 1 ).fill (0n );
497+ for (let i = 1 ; i <= n ; i ++ ) {
498+ this .p [i ] = (this .p [i - 1 ] * base ) % mod ;
499+ this .h [i ] = (this .h [i - 1 ] * base + BigInt (s .charCodeAt (i - 1 ))) % mod ;
500+ }
501+ }
502+
503+ query(l : number , r : number ): bigint {
504+ return (this .h [r ] - ((this .h [l - 1 ] * this .p [r - l + 1 ]) % this .mod ) + this .mod ) % this .mod ;
505+ }
506+ }
507+
508+ function partitionString(s : string ): string [] {
509+ const n = s .length ;
510+ const hashing = new Hashing (s );
511+ const vis = new Set <string >();
512+ const ans: string [] = [];
513+ let l = 1 ;
514+ for (let r = 1 ; r <= n ; r ++ ) {
515+ const x = hashing .query (l , r ).toString ();
516+ if (! vis .has (x )) {
517+ vis .add (x );
518+ ans .push (s .slice (l - 1 , r ));
519+ l = r + 1 ;
520+ }
521+ }
522+ return ans ;
523+ }
217524```
218525
219526<!-- tabs: end -->
0 commit comments