Grade My Scan-tron!

Stacked, 68 + 1 = 69 bytes

'|'split[#'1-]NO neq::size:@z~>*[]YES' '#`out is0 sum z/100*1 nround

Try it online! +1 for -p flag (this script can be executed as stacked -pe "...")

Takes two inputs from the top of the stack.

Some interesting features:

[#'1-]NO
[    ]NO   do not keep members where
 #'          its length
   1-          -1
             is truthy (in this case, not equal to zero).

This yields all letters surrounded by pipes.

:size:@z~>*[]YES
:                 duplicate indices of incorrect answers
 size             length of incorrect answers
     :@z          (stored into z)
        ~>        range from 1 to this length
          *       and multiply by this range
           []YES  keep truthy elements

This gives us all incorrect question numbers.


Python 2, 94 93 bytes

-1 byte thanks to L3viathan

s,a=input()
l=len(s)
w=[i+1for i in range(l)if"|%s|"%a[i]not in s[i]]
print(l-len(w))*1e2/l,w

Try it online!


Pip, 49 46 44 48 45 bytes

Ugh, that rounding takes so many bytes... 44 bytes of code, +1 for -s flag.

(/2+m-m/#b*#P_FI{++nbNa?un}MZa@`\|..`b)//1/t

Takes input as command-line arguments (the scan-tron page will need quoting and escaping of newlines if you run it from an actual command line). Outputs the missed questions first, then the score. Try it online!

Explanation

I'm gonna do this in two parts: the incorrect questions list and the score.

P_FI{++nbNa?un}MZa@`\|..`b
                            a,b are cmdline args, u is nil, n is newline (implicit)
                            Note that a string like n, in math contexts, is equivalent to 0
                 a@`\|..`   Find all occurrences in a of | followed by 2 chars
                            Because regex matches don't overlap, this does what we need
    {         }MZ        b  Zip with b and map this function to each pair of items:
     ++n                     Increment n (so the first time through, it's 1)
        bNa                  Is 2nd arg a substring of 1st?
           ?un               If so, return nil; if not, return n
                            Now we have a list containing nil for correct questions
                            and the question number for incorrect questions
 _FI                        Filter on identity function (keep only truthy values)
P                           Print, joining on spaces (-s flag)

(/2+m-m/#b*#...)//1/t
                       a,b are cmdline args, m is 1000 (implicit)
            ...        The code from the first part
           #           Length of that list (i.e. number of incorrect questions)
      m/#b*            Times 1000/(number of questions)
    m-                 Subtracted from 1000
 /2+                   Plus 1/2 (= 0.5)
                       We now have a number like 667.1666666666667
(              )//1    Int-divide by 1 to truncate
                   /t  and divide that by 10
                       Print (implicit)