Tut-tut-tut-tut-tut

The string tut-tutut-tut-tuttut-tut can be constructed with overlapping or concatenated instances of the word tut-tut:

tut-tutut-tut-tuttut-tut
tut-tut
tut-tut
tut-tut
tut-tut


The string tut-tututut-tutut-tut cannot:

tut-tututut-tutut-tut
tut-tut^
|tut-tut
|      tut-tut
|
this u is unaccounted for


Given a string, determine whether it's constructed of overlapping or concatenated instances of the word tut-tut. Where two or more tut-tut strings overlap they must share the same letters in the same positions.

Rules

• Standard I/O rules apply. You can use any two distinct, consistent values to distinguish the true and false cases.
• You may assume the input strings are nonempty and only contain lowercase ASCII letters and -.
• This is code golf, shortest code in bytes wins.

Test Cases

True:

tut-tut
tut-tut-tut
tut-tutut-tut
tut-tuttut-tut
tut-tut-tut-tut-tut
tut-tutut-tut-tutut-tut
tut-tutut-tut-tuttut-tut
tut-tut-tutut-tuttut-tut
tut-tuttut-tutut-tut-tutut-tut


False:

x
tut
tut-
-tut
t-tut
tu-tut
tut-tutx
tut-tutt
xtut-tut
ttut-tut
tutut-tut
tut-tu-tut
tut-tuttut
tuttut-tut
tut-tut-tutt
tut-tutt-tut
tut-tuttut-tu
tut-tututut-tut
tut-tuttuttut-tut
tut-tututut-tutut-tut

• Did you mean to have a double t in the first example?
– xnor
Sep 8 at 0:07
• @xnor The intention is that the strings can be overlapped or concatenated, but I agree that's not very clear, I'll edit to clarify Sep 8 at 0:10
• suggest tut-tut-tutt as a test case
– att
Sep 8 at 0:25
• Wait this needs way more test cases, tut-tutt-tut for falsy :P Sep 8 at 1:55
• this is a great challenge though :-) it seems to have a surprising number of approaches Sep 8 at 2:22

Regex (POSIX ERE / RE2 or better), 2321 20 bytes

^\b((t?\But)?-tut)+$ Based on regexes by thejonymyster and Bubbler. Try it on regex101! - RE2 Try it online! - GNU ERE (Bash + egrep) Try it online! - ECMAScript Try it online! - Perl Try it online! - Raku:P5 Try it online! - PCRE Try it online! - Java Try it online! – Try It Online") - Boost Try it online! - Python Try it online! - Ruby Try it online! - .NET ^ # Anchor to start of string. \b # Assert there is a word boundary here. Given the context, this is # equivalent to asserting that the first character isn't "-". ( ( t? # Optionally match and consume "t". \B # Assert that this is not a word boundary. Given the context, this # is equivalent to asserting that the string does not start here. ut # Match and consume "ut". )? # Optionally match the above. -tut # Match and consume "-tut". )+ # Iterate the above as many times as possible, minimum one.$            # Assert we've reached the end of the string.


Alternative 20 bytes:

^(tut-(tu\Bt?)?)+\b$ Try it on regex101! - RE2 Try it online! - ECMAScript Try it online! - PCRE This version doesn't work properly in GNU ERE; it matches tut-tuttut-tu. This appears to be a bug in GNU ERE, and is still present in the latest version. Note that ^(a\Bb?){2}$ incorrectly matches aba:

Try it online! - GNU ERE (bug) / Attempt This Online! (bug)
Try it online! - PCRE (no bug)

But ^a\Bb?a\Bb?$ correctly doesn't match aba: Try it online! - GNU ERE / Attempt This Online! 21 bytes without word-boundary assertions (GNU ERE or better): ^(tut-(tut?|))+tut$\2


21 bytes without word-boundary assertions or backreferences (ECMAScript or better):

^(?=t)((t?ut)?-tut)*$ Regex (ECMAScript or better), 22 bytes ^((?=tut-tut).{1,7})*$


Try it online! - ECMAScript
Try it online! - Perl
Try it online! - Raku:P5
Try it online! - PCRE
Try it online! - Java
Try it online! - Boost
Try it online! - Python
Try it online! - Ruby
Try it online! - .NET

^                 # Anchor to start of string
(
(?=tut-tut)   # Assert that the following 7 characters match "tut-tut",
# without consuming them.
.{1,7}        # Skip anywhere from 1 to 7 characters
)*                # Iterate the above as many times as possible
$# Assert we've reached end of string  Alternative 22 bytes: ^(t(?=ut-tut).{0,6})*$


Regex (Python / Ruby), 21 bytes

^((?=tut-tut).{,7})*$ Try it online! - Python Try it online! - Ruby Python and Ruby support the quantifier shorthand of {,N} to mean {0,N}. Alternative 21 bytes: ^(t(?=ut-tut).{,6})*$


Try it online! - Python
Try it online! - Ruby

Regex (Raku), 25 21 bytesSBCS

^tut»((t?ut)?\-tut)+$ Ported from a previous (21 byte) version of the "POSIX ERE / RE2 or better" regex. Try it online! Porting the 21 byte lookahead-using version comes to 28 bytes: ^<?before t>((t?ut)?\-tut)*$


Try it online!

{/^tut»((t?ut)?\-tut)+$/}  Try it online! Ruby, 29 28 bytes ->s{~/^\b((t?\But)?-tut)+$/}


Try it online!

s=>/^\b((t?\But)?-tut)+$/.test(s)  Try it online! PowerShell, 35 34 bytes $args-match'^\b((t?\But)?-tut)+$'  Try it online! Java, 36 bytes s->s.matches("((?=tut-tut).{1,7})*")  Try it online! s->s.matches("tut\\b((t?ut)?-tut)+")  Try it online! s->s.matches("\\b((t?\\But)?-tut)+")  Try it online! Julia v1.2+, 3938 37 bytes s->endswith(s,r"^\b((t?\But)?-tut)+")  Attempt This Online! R, 40 bytes \(L)grepl('^((?=tut-tut).{1,7})*$',L,,1)


Attempt This Online!

\(L)grepl('^tut\\b((t?ut)?-tut)+$',L,,1)  Attempt This Online! \(L)grepl('^\\b((t?\\But)?-tut)+$',L,,1)


Attempt This Online!

fn($s)=>preg_match('/^\b((t?\But)?-tut)+$/',$s)  Try it online! Python, 54 53 bytes lambda s:re.match(r'\b((t?\But)?-tut)+$',s);import re


Try it online!

^\b((t?\But)?-tut)+$ Try it online! Pip, 2524 21 bytes ^\b((t?\But)?-tut)+$Na
+(t?\But|\b)-tut~=a


Try it online!

Thanks to DLosc for demonstrating this syntax. This is apparently shorthand for ^((t?\But|\b)-tut)+$. This regex is slightly less inefficient speed-wise, but by moving all distinctive operations inside the loop, it allows Ruby-n, 25 24 bytes p~/^\b((t?\But)?-tut)+$/


Try it online!

Prints nil for false and 0 for true, which are respectively falsey and truthy in Ruby. If printing 0 or 1 is desired, replace p~ with p !! (+2 bytes).

Perl-p, 2726 25 bytes

$_=/^\b((t?\But)?-tut)+$/


Try it online!

'^\<((t?\But)?-tut)+$'XXn  Try it online! MATL appears to have \< and \> as its word boundary assertions, and apparently doesn't support \b, yet somehow still supports \B. I can't currently explain this; I thought it used PCRE. Zsh, 35 34 bytes [[$1 =~ '^\<((t?\But)?-tut)+$' ]]  Try it online! Zsh uses GNU ERE as its regex engine, so while \b would work, \< should be slightly more efficient due to only checking for the type of word boundary that can occur at that location. PHP-F, 4847 46 bytes <?=preg_match('/^\b((t?\But)?-tut)+$/',$argn);  Try it online! - bare-bones test harness Try it online! - fancier test harness • wow, i just wrote a different 22 byter myself Sep 8 at 1:20 • no actually :o perhaps ill just share it here for completeness since all pure regexes are bound to be similar :-) ^(?=tu)(t?(ut)?-tut)+$ in js regex, correct me if its wrong though. regexr link Sep 8 at 1:29
• @thejonymyster Oh wow! That is very different. Amazing that it turned out to be exactly the same length. Sep 8 at 1:31
• It was also wrong :-) false positives on tut-tutt-tut EDIT: easily fixed though, misplaced paren on my part Sep 8 at 1:54
• "True" regex ^tut-tut((t?ut)?-tut)*$ is only one byte longer than the first one. Sep 8 at 2:05 Zsh, 66 54 bytes -6 bytes thanks to Neil, -6 bytes by switching to -e thanks to pxeger. s=xxxxxx$1
repeat $#1 [[${s:$[i++]:13} = *tut-tut* ]]  The problem stated is equivalent to: Every sliding 13-character window of xxxxxx[string]xxxxxx contains tut-tut. In Bash and Zsh, the ${var:${idx}:13} is valid even if the string isn't long enough, so the trailing xxxxxx are unneeded. We loop over each window, and exit out if any single test fails thanks to errexit. • You can use bye instead of exit for -1 byte Sep 8 at 7:41 • In fact, if you use Zsh -e, you can remove ||exit completely Sep 8 at 7:41 • The six trailing xs seem unnecessary. – Neil Sep 8 at 8:39 APL (Dyalog Classic), 21 bytes ∧/7∨/'tut-tut'⍷6∘↓⍣¯1  Try it online!  6∘↓⍣¯1 pad with leading spaces 'tut-tut'⍷ find occurrences of tut-tut ∧/7∨/ present in every group of 7 consecutive positions  Prolog (SWI), 70 bytes b-->"tut-tut". n-->(b;"ut-tut";"-tut"),(n;""). t-->b;b,n. +X:-t(X,[]).  Try it online! -4 from jo king. • Prolog also supports regex, which can be used to match against the string for 41 bytes – Jo King Sep 9 at 7:53 • I think phrase is superfluous, it can just be t(X,[]) – Jo King Sep 9 at 7:58 • @JoKing yeah, the regex one i wasn't interested in. It'll just make deadcode's answer longer. I didn't know that phrase wasn't needed. will change that. Sep 9 at 8:21 Python3, 61 bytes: f=lambda x:x==''or'tut-tut'==x[:6|f(x[4:])|f(x[6:])|f(x[7:])]  Try it online! • 70: f=lambda x:x==''or'tut-tut'==x[:7]and any(f(x[i:])for i in range(1,8)) Sep 8 at 0:59 • I think you can replace and  with * though it's slow – xnor Sep 8 at 3:50 • And you only need to check displacements 4, 6, and 7: 61 bytes – xnor Sep 8 at 7:47 • @xnor Thank you, updated Sep 8 at 12:49 Python, 50 bytes def f(s):s[:7]=="tut-tut"and f(s[~ord(s[7])%5|4:])  Signals by exit code: errors out if the input is "tut-tut"-decomposible. How? Recursively checks that input starts with "tut-tut". Based on the next character directly computes where the next "tut-tut" has to start. 0 1 2 3 4 5 6 7 8 9 ... --- t u t - t u t | - | | | ==> t u t | - | t u t --- --- t u t - t u t | u | | | ==> t | u | t - t u t --- --- t u t - t u t | t | | | ==> | t | u t - t u t ---  The figure shows offsets inferred from char at position 7: • "-" --> 4 • "u" --> 6 • "t" --> 7 The magic formula ~x%5|4 implements this map. x is the code point of input character. Importantly, any input is mapped to 4,5,6,7, so the algorithm can neither get stuck nor skip any characters. Now, if we can cover the input with "tut-tut"s we will eventually reach the end and error out as soon as we try to access out-of-bounds characters. Otherwise the algorithm will simply stop at the first place that cannot be covered. Attempt This Online! Thanks to eagle-eyed @Jonathan Allan for spotting a problem with my first attempt. Wrong Python, 54 bytes def f(s):s[:7]=="tut-tut"and f(s[~ord(s[7])%31%12%8:])  Attempt This Online! • @JonathanAllan I guess you are right. Back to the drawing board... Sep 8 at 17:33 • Are there actually any inputs that result in an infinite loop? It appears that tut-tutc returns a false positive but does at least halt. Sep 8 at 17:34 • @Deadcode looks like a maximum recursion depth error to me. Sep 8 at 17:45 • Ah, indeed. I forgot that the recursion error would be silenced in your ATO link due to the try/except being indiscriminate about which types of exceptions it intercepts. Sep 8 at 17:51 • The problem with your first attempt being that it could get stuck if the formula evaluated to 0? – Neil Sep 9 at 6:30 Vyxal, 1614 13 bytes mp13l‛≠₈‹∞vcg  Returns 1 for true, or falsy for false (0 or an empty list). mp13l‛≠₈‹∞vcg m # Mirror the input - append its reverse p # Append the input to that 13l # Get all overlapping strings of length 13 ‛≠₈‹∞vc # For each, does it contain "tut-tut"? g # Minimum. Returns an empty list for an empty list (no idea why)  -3 from porting Jonathan Allan's Jelly answer! -1 thanks to emanresu A. Previously: Vyxal, 16 bytes 6Iø.13l≠₈-∞vcA  Try it Online! Uses the same idea as @GammaFunction's Zsh answer. 6Iø.13l≠₈-∞vcA 6Iø. # Surround the input with 6 spaces on both sides 13l # Get all overlapping strings of length 13 ≠₈-∞vcA # Do all of them contain "tut-tut"?  • 13 Sep 9 at 19:56 • Ah nice, forgot about that useless overload. Sep 9 at 21:48 • BTW, the question specifies "You can use any two distinct, consistent values to distinguish the true and false cases," which doesn't allow turning falsey inputs into 0 sometimes and empty list other times. Sep 15 at 16:18 Curry (PAKCS), 42 bytes f""=1 f(a@(_:_++b)++c)|a=="tut-tut"=f$b++c


Try it online!

Returns 1 for truth, and nothing otherwise.

Raku, 42 bytes

{^.comb⊆m:ex/tut\-tut/>>.&{.from..^.to}}


Try it online!

An anonymous function that takes a string and returns a true/false value for if it is composed of overlapping 'tut-tut's.

Explanation

{                                      } # Anonymous function block
^.comb    # The range 0 to length of input
⊆   # is a subset or equal to
m:ex/tut\-tut/                  # Match all instances of 'tut-tut'
>>.&{           } # And map them to

.from..^.to  # The range of covered indexes


This basically finds the indexes covered by each 'tut-tut' and makes sure that they cover the entire string. Note that you could easily generalise this function to any string.

Husk, 24 23 bytes

Edit: saved 1 byte based on Dominic van Essen's comment.

±Λ≤7Ẋ-J¥¨μζ-μζ¨X7¹e_6→L


Try it online!

Explanation

±Λ≤7Ẋ-J¥¨μζ-μζ¨X7¹e_6→L

±Λ≤7                          check if all differences are ≤7
Ẋ-                        take the difference of all adjacent elements in list
J                       put indices list the into the middle of [-6,1+len(input)]
¥                      create indices list of all occurrences of "tut-tut" in substring list
¨μζ-μζ¨               "tut-tut"
X7¹           create list of substrings of input with length 7
e_6→L      list [-6,1+len(input)]

• Nice answer & welcome to code golf! You can also save a byte by using the compressed string ¨μζ-μζ¨ for "tut-tut" instead of building it with hD"tut-"... Sep 9 at 7:51
• Thank you! :) I updated my answer accordingly. Sep 9 at 11:54

sed, 39 bytes

:a
s/tut-tut/TUTTUT/i
ta
s/[^TU]//
T
cN


Truthy test cases -- outputs nothing

Falsy test cases -- outputs one N for each line

The idea here is quite simple:

• Continuously replace tut-tut with TUTTUT, using case-insensitive matching, until the input stops changing.
• Check if there are any lowercase characters or - left.
• If so, output an N for failure. Otherwise, output nothing for success.

sed, 29 bytes, straight regex, thanks to Deadcode

s/^((t?\But)?\b-tut)+\$//
t
cN


Attempt This Online!

Thanks to Deadcode for this alternate approach which forgoes looping and solves it with a single application of a true regex.

• This is creative, but I feel obligated to point out that the same effect can be achieved with the same flags in 33 bytes Sep 8 at 17:27
• Very much appreciated! I will have to update it a bit later today as I am in the middle of work stuff. Sep 8 at 17:30
• Why would the /i version loop infinitely? Surely it would stop when it runs out of -s. Also, I can't get the ATO link to work, it outputs N for any input.
– Neil
Sep 9 at 6:26
• @Neil re: ATO, it is working for me (on original and now-updated code). I separated truthy/falsy links to make the expectations clearer. Lmk if that works for you. Re: looping, you are correct! The looping problem was happening on earlier version, and I forgot to recheck when I updated to remove the -. Code and explanation are now updated. Thanks! Sep 9 at 13:13
• I see now, the truthy cases output nothing at all, so when you had a mix of truthy and falsy cases, you couldn't tell which ones were truthy. Very confusing.
– Neil
Sep 9 at 16:20

05AB1E, 17 bytes

žO.øü13"tut-"ûδåP


Port of @GammaFunction's Zsh answer, so make sure to upvote him/her as well!

Explanation:

žO                # Push constant string "aeiouy"
.ø              # Prepend and append this string to the (implicit) input
ü13           # Pop and create overlapping parts of size 13
"tut-"û    # Push string "tut-" and palindromize it to "tut-tut"
δ   # Map over each overlapping part using "tut-tut" as argument:
å  #  Check if the part contains "tut-tut" as substring
P # Product to check if all are truthy
# (after which the result is output implicitly)


JavaScript (ES6), 58 bytes

A non-regex solution.

f=([c,...a],i)=>i>6?!c:c=="tut-"[i&3]&&f(a,-~i)|3%~-i&f(a)


Try it online!

How?

We use a pointer $$\i\$$ into the string $$\s=\$$ "tut-tut". At each iteration, we make sure that the current character from the input string matches $$\s[i]\$$, then we either increment $$\i\$$ or restart with $$\i=0\$$ if we are allowed to do so. The parsing is successful if we end up with $$\i=7\$$ exactly when the end of the input string is reached.

The table below summarizes after which positions it's possible to restart with $$\i=0\$$ and how this can be computed with the expression 3 % ~-i & 1.

i = position character restart after? 3 % ~-i 3 % ~-i & 1
0 t No 0 0
1 u No NaN 0
2 t No 0 0
3 - Yes 1 1
4 t No 0 0
5 u Yes 3 1
6 t Yes 3 1

Brachylog, 20 bytes

Ẹ|a₀"tut-tut"sl;?b₍↰


Try it online!

Explanation

This is a pretty straight-at-the-problem approach; possibly something more subtle would be shorter.

Ẹ|a₀"tut-tut"sl;?b₍↰
Ẹ                     Either the input is the empty string
|                    Or
a₀                  A prefix of the input
"tut-tut"         is "tut-tut"
s        Take a nonempty substring of "tut-tut"
l       Get its length
;?b₍   Remove that many characters from the start of the input string
↰  Recurse


Husk, 2218 17 bytes

Edit: saved 4 5 bytes based on Jonathan Allan's idea

▼M€X13‼S+↔⁰¨μζ-μζ


Try it online!

      ‼   ⁰             # apply twice to the input:
S+↔              #   join to itself reversed
X13                  # and split this into strings of 13 characters;
M                      # now map over these
€                     #   do they contain
¨μζ-μζ¨      #   the compressed string "tut-tut"?
▼                       # and return the minimum result

• @Calga - I couldn't get that to work, because the implicit ⁰ gets added at the end, instead of directly after the D+¹↔, and if I re-arrange using m and putting D+¹↔ last, I need to include the closing ¨ after the compressed string. Or am I missing a trick...? Sep 9 at 13:47
• No, you are right, my bad. Deleted my comment, but apparently too late. Sep 9 at 13:52

JavaScript (V8), 39 bytes

x=>x.replace(/(?=tu)((t?ut)?-tut)*/,"")


Try it online!

Returns a falsy string if the string is correctly composed of tut-tuts, and a truthy string otherwise.

Mostly the same logic as my (slightly broken) regex solution, except instead of having the "this regex matches the entire string or else it fails" logic be in the regex, its in the fact that we only delete one occurrence of the pattern. If the entire string is not composed of the pattern, there will be string remaining after the replacement, and in js, any nonempty string is truthy, with the empty string being the only falsy string :-)

(in case you wonder why this doesn't fail for cases where two valid strings are concatenated: regex is greedy, and will grab the largest match it can grab)

Breakdown:

x=>x.replace(/(?=tu)((t?ut)?-tut)*/,"") // full function

x=>x.replace(/                    /,"") // return the string with exactly one
// match of the following regex removed
(?=tu)                    //   string starting with "tu"
(           )*      //   which contains at least 0 of
//   the following pattern
//   (actually at least 1, since otherwise,
//   the pattern would be empty and
//   thus would fail the "tu" check)
(t?ut)?-tut        //     any of "-tut"
//     (will never be at the start due to the
//     "tu" check, but can appear in cases
//     where the entire "tut" overlaps),
//     "ut-tut"
//     (in cases where just the "t" is overlapping,
//     also can never be at the start)
//     or "tut-tut"
//     (this one always comes first
//     due to the "tu" check)



BTW this regex saves a byte over the straightforward regex /tut-tut((t?ut)?-tut)*/ because we don't actually have to use the entire "tut-tut"; the first group ((t?ut)?-tut) is forced to complete to tut-tut once it's forced to start with tu, if that makes sense... :-)

• -4 bytes if i get off my lazy ass and switch from x.replace(/.../,"") to /.../.test(x) but im tired of generating tio links and changing code breakdowns etc Sep 8 at 2:40

Factor, 36 bytes

[ R/ tut\b((t?ut)?-tut)+/ matches? ]


Try it online!

Factor + math.unicode, 63 bytes

[ "xxxxxx"dup surround 13 clump [ "tut-tut"swap subseq? ] ∀ ]


Try it online!

Charcoal, 21 bytes

⬤θ№✂⁺×⁶ψθκ⁺¹³κ¹…tut-⁷


Try it online! Link is to verbose version of code. Outputs a Charcoal boolean, i.e. - for a valid string, nothing if not. Explanation: A port of my golf to @GammaFunction's zsh answer.

 θ                      Input string
⬤                       All indices satisfy
⁶                 Literal integer 6
×                  Repetitions of
ψ                Null byte
⁺                   Plus
θ               Input string
✂          ¹         Sliced from
κ              Current index to
¹³           Literal integer 13
⁺             Plus
κ          Current index
№                     Contains
tut-    Literal string tut-
…        Extended to length
⁷   Literal integer 7


MathGolf, 24 bytes

☼▌☼▐l£{_C<ÿtut-ñ╧\╞};]ε*


Port of @GammaFunction's Zsh answer, so make sure to upvote him/her as well!

Try it online.

Explanation:

☼▌               # Prepend a leading "100000" in front of the (implicit) input-string
☼▐             # Append "100000" as well
l£               # Push the input-string, pop and push its length
{            } # Loop that many times:
_             #  Duplicate the current string
C<           #  Pop the copy, and keep its first 13 characters
ÿtut-ñ     #  Push string "tut-", and palindromize it to "tut-tut"
╧    #  Check if the string contains this "tut-tut" as substring
\   #  Swap so the string we've duplicated is at the top of the stack again
╞  #  Remove its leading character
;                # After the loop, discard the remaining string from the stack
]               # Wrap everything else on the stack into a list
ε*             # Product (reduce by multiplication) to verify whether all checks were
# truthy
# (after which the entire stack is output implicitly as result)


Jelly, 16 bytes

m0;w13Ƥ“"ṁĿ4ṘṘ»Ṃ


A monadic Link that accepts a list of characters and yields 1 if constructible or 0 if not.

Try it online!

How?

The input must (a) start with tut-tut, (b) end with tut-tut, and (c) have tut-tut as a contiguous substring of all of its length $$\13\$$ slices. We can, however, just test that (c) holds for the input concatenated with its reverse concatenated with the input (e.g. abc -> abccbaabc). This works since tut-tut is palindromic and any bad starts or bad ends in the input will now nest between any tut-tuts.

m0;w13Ƥ“"ṁĿ4ṘṘ»Ṃ - Link: list of characters, S e.g. "abc"
m0               - reflect -> "abccba"
;              - concatenate -> "abccbaabc"
Ƥ          - for infixes...
13           - ...of length 13:
w   “"ṁĿ4ṘṘ»  -   first 1-indexed index of "tut-tut" or 0 if not found
Ṃ - minimum (an empty list yields 0)


Bit of a shame it takes half the code to make "tut-tut"!

Rust, 101 bytes

|s:&str|(0..s.len()).scan(0,|r,i|{if s[i..].starts_with("tut-tut"){*r=7}*r-=1;Some(*r)}).all(|i|i>=0)


Playground

Returns a boolean with the obvious meaning.

C (gcc), 94 bytes

r;d;b;f(char*s){for(b=r=3;r&&s[3]|b;b=0)d=strstr(s,"tut-tut")-s,s+=d&~3|d==1|d&b?r=0:d+4;b=r;}


Try it online!

Inputs a pointer to a string.
Returns $$\3\$$ if the input is constructed of overlapping or concatenated instances of the string "tut-tut" or $$\0\$$ otherwise.

Japt, 16 bytes

pÔiU ãD eøt©-t©


Try it

pÔiU ãD eøt©-t©     :Implicit input of string U
p                    :Append
Ô                   :  Reverse
iU                 :Prepend input
ã               :Substrings of length
D              :  13
e            :All
ø           :  Contain
t©-t©     :    Compressed string "tut-tut"
`