Mutually fill in the blanks

Retina, 102 100 93 88 bytes

Byte count assumes ISO 8859-1 encoding.

$
!¶$`
m`(?<=^(\w+ )*)(?=_.*¶(?<-1>\w+ )*(\w+))
$2
(([a-z])+)(?<-2>_)*(_*)\3|!\D+
$3$1$3

The strings will are separated by a linefeed. If there is an odd number of underscores left, the extraneous one will be after the word.

Try it online!

Explanation

I guess this is the "duplicate-append-lookback-match-add-centre approach", or something close...

$
!¶$`

We start by duplicating the input (separated with a ! and a linefeed). The purpose of this is that we can then process both lines by fetching words from the next line (instead of having to treat the second line separately).

m`(?<=^(\w+ )*)(?=_.*¶(?<-1>\w+ )*(\w+))
$2

This prepends the correct word to each gap. We start by counting the current word position with the lookbehind (?<=^(\w+ )*) (the position is stored as the depth of group 1). Then the lookahead a) ensures that we're at the beginning of a gap by matching _, then skips to the next line with .*¶, matches the correct number of words with (?<-1>\w+ )* to get to the right position, and then matches the word found there with (\w+) into group 2.

(([a-z])+)(?<-2>_)*(_*)\3|!\D+
$3$1$3

This stage does three things:

  • It removes the underscores corresponding to each word length. This is done by counting the word length into group 2 with ([a-z])+ and then matching that many underscores (which are never written back).
  • It shifts the word to the centre of the gap by capturing half of the remaining underscores with (_*)\3 and writing $3$1$3 back.
  • It removes the duplicated input by matching !\D+ and replacing it with nothing.

Pyth, 30

jL;Cmm|*}J\_k.[lkhx#JdJkdCcR;Q

Takes input and outputs as a list of two strings. Uses a pretty basic split–zip–double map–centre–zip–join approach.

Try it here

Expanded:

jL;Cmm|*}J\_k.[lkhx#JdJkdCcR;Q   ##
                          cR;Q   ##  split
                         C       ##  zip
    mm                           ##  double map
      |*}J\_k.[lkhx#JdJkd        ##  centre
   C                             ##  zip
jL;                              ##  join

I'll explain more once I'm really satisfied that I can't golf this any more, although it should be pretty clear, given the ubiquity of the split–zip–double map–centre–zip–join approach and all.


Python 2, 109

def f(a,b):exec"print' '.join([x,y][x<'`'].center(len(x),'_')for x,y in zip(a.split(),b.split()));a,b=b,a;"*2

The function takes two strings as arguments and prints the output as in the examples. It uses a boring approach, with str.center(width, fillchar) doing most of the work.

Try it online.